diff --git a/README.md b/README.md
index 9272bcee26..fdff7a9055 100644
--- a/README.md
+++ b/README.md
@@ -13,22 +13,18 @@ Agama is a new Linux installer born in the core of the YaST team. It is designed
re-usability, integration with third party tools and the possibility of building advanced user
interfaces over it.
-| | |
-| -------------------------------------------------------------------- | --------------------------------------------------------------- |
-|  |  |
+|  |  |
+| --- | --- |
Click to show/hide more screenshots
---
-| | |
-| ------------------------------------------------------------ | -------------------------------------------------------------- |
-|  |  |
-
-| | |
-| ------------------------------------------------------ | --------------------------------------------------------------- |
+|  |  |
+| --- | --- |
|  |  |
+
diff --git a/Rakefile b/Rakefile
index 48cad8f7d0..0e4685d4a8 100644
--- a/Rakefile
+++ b/Rakefile
@@ -19,9 +19,12 @@
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.
+$LOAD_PATH.unshift File.expand_path("service/lib", __dir__)
+
require "shellwords"
require "fileutils"
require "yast/rake"
+require_relative "service/lib/tasks/autoyast"
# Infers the gem name from the source code
#
@@ -209,3 +212,11 @@ if ENV["YUPDATE_FORCE"] == "1" || File.exist?("/.packages.initrd") || live_iso?
end
end
end
+
+desc "Documentation tasks"
+namespace :doc do
+ desc "Generate the AutoYaST compatibility documentation"
+ task :autoyast_compat do
+ puts Agama::Tasks::AutoYaSTCompatGenerator.new.generate
+ end
+end
diff --git a/doc/images/screenshots/overview.png b/doc/images/screenshots/overview.png
deleted file mode 100644
index d9d3447197..0000000000
Binary files a/doc/images/screenshots/overview.png and /dev/null differ
diff --git a/doc/images/screenshots/product-selection.png b/doc/images/screenshots/product-selection.png
deleted file mode 100644
index 0dae2ed44c..0000000000
Binary files a/doc/images/screenshots/product-selection.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/cockpit_login.png b/doc/images/screenshots/s390/cockpit_login.png
deleted file mode 100644
index cc4895b01f..0000000000
Binary files a/doc/images/screenshots/s390/cockpit_login.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/configure_dasd.png b/doc/images/screenshots/s390/configure_dasd.png
deleted file mode 100644
index b364b990a2..0000000000
Binary files a/doc/images/screenshots/s390/configure_dasd.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/dasd_activate.png b/doc/images/screenshots/s390/dasd_activate.png
deleted file mode 100644
index e2a701318e..0000000000
Binary files a/doc/images/screenshots/s390/dasd_activate.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/dasd_format.png b/doc/images/screenshots/s390/dasd_format.png
deleted file mode 100644
index 1f8efb616a..0000000000
Binary files a/doc/images/screenshots/s390/dasd_format.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/dasd_list.png b/doc/images/screenshots/s390/dasd_list.png
deleted file mode 100644
index 90281b2a5d..0000000000
Binary files a/doc/images/screenshots/s390/dasd_list.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/dasd_list_diag.png b/doc/images/screenshots/s390/dasd_list_diag.png
deleted file mode 100644
index eb0b21ac83..0000000000
Binary files a/doc/images/screenshots/s390/dasd_list_diag.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/dasd_list_refreshed.png b/doc/images/screenshots/s390/dasd_list_refreshed.png
deleted file mode 100644
index 1805a7474b..0000000000
Binary files a/doc/images/screenshots/s390/dasd_list_refreshed.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/dasd_multiple_select.png b/doc/images/screenshots/s390/dasd_multiple_select.png
deleted file mode 100644
index 5a522d36f4..0000000000
Binary files a/doc/images/screenshots/s390/dasd_multiple_select.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/dasd_use_diag.png b/doc/images/screenshots/s390/dasd_use_diag.png
deleted file mode 100644
index f90a1f71e0..0000000000
Binary files a/doc/images/screenshots/s390/dasd_use_diag.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/format_progress.png b/doc/images/screenshots/s390/format_progress.png
deleted file mode 100644
index 8c7c2c905b..0000000000
Binary files a/doc/images/screenshots/s390/format_progress.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/format_progress_multiple.png b/doc/images/screenshots/s390/format_progress_multiple.png
deleted file mode 100644
index 238772f999..0000000000
Binary files a/doc/images/screenshots/s390/format_progress_multiple.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/installation_summary.png b/doc/images/screenshots/s390/installation_summary.png
deleted file mode 100644
index 21e6687876..0000000000
Binary files a/doc/images/screenshots/s390/installation_summary.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/product_selection.png b/doc/images/screenshots/s390/product_selection.png
deleted file mode 100644
index f74f4c6f79..0000000000
Binary files a/doc/images/screenshots/s390/product_selection.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/sles_rexx.png b/doc/images/screenshots/s390/sles_rexx.png
deleted file mode 100644
index cc5d8cd439..0000000000
Binary files a/doc/images/screenshots/s390/sles_rexx.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/storage_section.png b/doc/images/screenshots/s390/storage_section.png
deleted file mode 100644
index c8cb83f365..0000000000
Binary files a/doc/images/screenshots/s390/storage_section.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/storage_summary.png b/doc/images/screenshots/s390/storage_summary.png
deleted file mode 100644
index 4012c10556..0000000000
Binary files a/doc/images/screenshots/s390/storage_summary.png and /dev/null differ
diff --git a/doc/images/screenshots/s390/zVM_login.png b/doc/images/screenshots/s390/zVM_login.png
deleted file mode 100644
index 003377e08c..0000000000
Binary files a/doc/images/screenshots/s390/zVM_login.png and /dev/null differ
diff --git a/doc/images/screenshots/software-page.png b/doc/images/screenshots/software-page.png
deleted file mode 100644
index 8dc972812b..0000000000
Binary files a/doc/images/screenshots/software-page.png and /dev/null differ
diff --git a/doc/images/screenshots/storage-page.png b/doc/images/screenshots/storage-page.png
deleted file mode 100644
index fc6a978bac..0000000000
Binary files a/doc/images/screenshots/storage-page.png and /dev/null differ
diff --git a/doc/questions.md b/doc/questions.md
index 7ef8ca3bfb..523d7755f1 100644
--- a/doc/questions.md
+++ b/doc/questions.md
@@ -63,9 +63,11 @@ Sensitive answers or params will be replaced, so the user has to explicitly spec
| class | description | possible answers | available data | notes |
|--- |--- |--- |--- |--- |
+| `autoyast.unsupported` | When there are unsupported elements in an AutoYaST profile | `Abort` `Continue` | `planned` elements to be supported in the future, `unsupported` unsupported elements | |
| `software.medium_error` | When there is issue with access to medium | `Retry` `Skip` | `url` with url where failed access happen | |
| `software.unsigned_file` | When file from repository is not digitally signed. If it should be used | `Yes` `No` | `filename` with name of file | |
| `software.import_gpg` | When signature is sign with unknown GPG key | `Trust` `Skip` | `id` of key `name` of key and `fingerprint` of key | |
| `storage.activate_multipath` | When it looks like system has multipath and if it should be activated | `yes` `no` | | Here it is used lower case. It should be unified. |
| `storage.commit_error` | When some storage actions failed and if it should continue | `yes` `no` | | Also here it is lowercase |
| `storage.luks_activation` | When LUKS encrypted device is detected and it needs password to probe it | `skip` `decrypt` | `device` name, `label` of device, `size` of device and `attempt` the number of attempt | Answer contain additional field password that has to be filled if answer is `decrypt`. Attempt data can be used to limit passing wrong password. |
+
diff --git a/live/Makefile b/live/Makefile
index cb1535e0d9..d192922f34 100644
--- a/live/Makefile
+++ b/live/Makefile
@@ -15,6 +15,11 @@ FLAVOR = openSUSE
# to use a different project run "make build OBS_PROJECT="
OBS_PROJECT = "systemsmanagement:Agama:Devel"
OBS_PACKAGE = "agama-installer"
+# to use internal OBS add "OBS_API=https://api.suse.de"
+OBS_API = "https://api.opensuse.org"
+# default OBS build target
+OBS_TARGET = "images"
+ARCH = $(shell uname -m)
# files to copy from src/
COPY_FILES = $(patsubst $(SRCDIR)/%,$(DESTDIR)/%,$(wildcard $(SRCDIR)/*))
@@ -52,9 +57,10 @@ $(DESTDIR)/%.tar.xz: % $$(shell find % -type f,l)
echo "$@ $${MSG}"
# build the ISO locally
+# allow passing optional parameters to osc like "-p " or "-k " via OSC_OPTS
build: $(DESTDIR)
- if [ ! -e $(DESTDIR)/.osc ]; then make clean; osc co -o $(DESTDIR) $(OBS_PROJECT) $(OBS_PACKAGE); fi
+ if [ ! -e $(DESTDIR)/.osc ]; then make clean; osc -A $(OBS_API) co -o $(DESTDIR) $(OBS_PROJECT) $(OBS_PACKAGE); fi
$(MAKE) all
- (cd $(DESTDIR) && osc build -M $(FLAVOR) images)
+ (cd $(DESTDIR) && osc -A $(OBS_API) build -M $(FLAVOR) $(OSC_OPTS) $(OBS_TARGET) $(ARCH) $(KIWI_FILE))
.PHONY: build all clean
diff --git a/live/README.md b/live/README.md
index a37116fa73..7d752c51b0 100644
--- a/live/README.md
+++ b/live/README.md
@@ -5,24 +5,27 @@
## Table of Content
- [Live ISO](#live-iso)
+ - [Table of Content](#table-of-content)
- [Layout](#layout)
- - [Building the Sources](#building-the-sources)
- - [Building the ISO Image](#building-the-iso-image)
- - [Build Options](#build-options)
- - [Image Definition](#image-definition)
- - [KIWI Files](#kiwi-files)
- - [Image Configuration](#image-configuration)
- - [GRUB2 menu](#grub2-menu)
- - [SSH Server](#ssh-server)
+ - [Building the sources](#building-the-sources)
+ - [Building the ISO image](#building-the-iso-image)
+ - [Build options](#build-options)
+ - [Using another project](#using-another-project)
+ - [Using internal build service](#using-internal-build-service)
+ - [Using locally built RPM packages](#using-locally-built-rpm-packages)
+ - [Image definition](#image-definition)
+ - [KIWI files](#kiwi-files)
+ - [Image configuration](#image-configuration)
+ - [GRUB2 menu](#grub2-menu)
+ - [SSH server](#ssh-server)
- [Autologin](#autologin)
- - [Firefox Profile](#firefox-profile)
+ - [Firefox profile](#firefox-profile)
- [Dracut menu](#dracut-menu)
- [Avahi/mDNS](#avahimdns)
- [The Default Hostname](#the-default-hostname)
- [Service Advertisement](#service-advertisement)
- - [The Default Cockpit/Agama TCP Port](#the-default-cockpitagama-tcp-port)
- - [Autoinstallation Support](#autoinstallation-support)
- - [Firmware Cleanup](#firmware-cleanup)
+ - [Autoinstallation support](#autoinstallation-support)
+ - [Firmware cleanup](#firmware-cleanup)
---
@@ -38,7 +41,7 @@ This directory contains a set of files that are used to build the Agama Live ISO
- [config-cdroot](config-cdroot) subdirectory contains file which are copied to the uncompressed
root of the ISO image, the files can be accessed just by mounting the ISO file or the DVD medium
-## Building the Sources
+## Building the sources
To build the sources for OBS just run the
@@ -56,7 +59,7 @@ make clean
or just simply delete the `dist` subdirectory.
-## Building the ISO Image
+## Building the ISO image
To build the ISO locally run the
@@ -70,7 +73,7 @@ build for output for the exact ISO file name.
For building an ISO image you need a lot of free space at the `/var` partition. Make sure there is
at least 25GiB free space otherwise the build will fail.
-### Build Options
+### Build options
By default this will build the openSUSE image. If you want to build another image then run
@@ -82,6 +85,8 @@ make build FLAVOR=openSUSE-PXE
See the [_multibuild](src/_multibuild) file for the list of available build flavors.
+#### Using another project
+
By default it will use the
[systemsmanagement:Agama:Devel](https://build.opensuse.org/project/show/systemsmanagement:Agama:Devel)
OBS project. If you want to build using another project, like your fork, then delete the `dist`
@@ -90,17 +95,42 @@ directory and checkout the OBS project manually and run the build:
```shell
rm -rf dist
# replace with your OBS account name
-osc co -o dist home::branches:systemsmanagement:Agama:Devel agama-installer-openSUSE
-make build
+make build OBS_PROJECT=home::branches:systemsmanagement:Agama:Devel
+```
+
+#### Using internal build service
+
+To build a SLE image using the internal OBS instance run
+
+```shell
+make build OBS_API=https://api.suse.de OBS_PROJECT= OBS_PACKAGE=agama-installer-SLE FLAVOR=SLE
```
-## Image Definition
+#### Using locally built RPM packages
+
+If you have a locally built RPM which you want to include in the ISO instead of the RPM from the
+build service use the `-p` osc option with directory containing the RPMS. The workflow should look
+like this:
+
+```shell
+# first create a place for storing the RPM packages, if the directory already
+# exists make sure it does not contain any previous results
+mkdir ~/rpms
+
+# build the updated RPM package locally and save the result into the created directory
+osc build -k ~/rpms
+
+# then build the Live ISO using these packages
+make build OSC_OPTS="-p ~/rpms"
+```
+
+## Image definition
The [KIWI](https://github.com/OSInside/kiwi) image builder is used by OBS to build the Live ISO. See
the [KIWI documentation](https://osinside.github.io/kiwi/index.html) for more details about the
build workflow and the `.kiwi` file format.
-### KIWI Files
+### KIWI files
The main Kiwi source files are located in the [src](src) subdirectory:
@@ -118,11 +148,11 @@ The main Kiwi source files are located in the [src](src) subdirectory:
- [fix_bootconfig](src/fix_bootconfig) - a special KIWI hook script which sets the boot
configuration on S390 and PPC64 architectures.
-## Image Configuration
+## Image configuration
The Live ISO is configured to allow using some features and allow running Agama there.
-## GRUB2 Menu
+## GRUB2 menu
grub.cfg, defining boot menu items of the Agama image, is generated by scripts stored in
[config-cdroot](https://github.com/openSUSE/agama/tree/master/live/config-cdroot).
@@ -130,8 +160,7 @@ grub.cfg, defining boot menu items of the Agama image, is generated by scripts s
Both x86_64 and aarch64 grub.cfg are basically copies of KIWI autogenerated grub.cfg.
The x86_64 grub.cfg contains UEFI fix for Booting from disk (Issue #1609).
-
-### SSH Server
+### SSH server
The SSH connection for the root user is enabled in the
[10_root_login.conf](root/etc/ssh/sshd_config.d/10_root_login.conf) file.
@@ -147,7 +176,7 @@ Automatic root login and staring the graphical environment is configured in seve
- Icewm uses the usual YaST2 installation [preferences.yast2](root/etc/icewm/preferences.yast2)
configuration file
-### Firefox Profile
+### Firefox profile
The default Firefox configuration is defined in the [profile](root/root/.mozilla/firefox/profile)
file. It disables several features which do not make sense in Live ISO like remembering the used
@@ -203,24 +232,13 @@ That allows scanning all running Agama instances in the local network with comma
avahi-browse -t -r _agama._sub._https._tcp
```
-### The Default Cockpit/Agama TCP Port
-
-The default Cockpit TCP port is 9090. That makes sense for the system management framework as the
-default ports might be used by a running Apache or other web servers.
-
-But Agama runs from a Live ISO where running a web server does not make much sense so we can safely
-use the default HTTP(S) ports.
-
-The default port is changed in the
-[listen.conf](root/etc/systemd/system/cockpit.socket.d/listen.conf) file.
-
-### Autoinstallation Support
+### Autoinstallation support
The autoinstallation is started using the [agama-auto](root/etc/systemd/system/agama-auto.service)
service which starts the [auto.sh](root/usr/bin/auto.sh) script. This script downloads the
installation profile, applies it to Agama and starts the installation.
-### Firmware Cleanup
+### Firmware cleanup
The [fw_cleanup.rb](root/tmp/fw_cleanup.rb) script removes the unused firmware from the image. Many
firmware files are not needed, this makes the final ISO much smaller.
diff --git a/live/config-cdroot/fix_bootconfig.aarch64 b/live/config-cdroot/fix_bootconfig.aarch64
index 4299976f0e..2deb71fc8a 100755
--- a/live/config-cdroot/fix_bootconfig.aarch64
+++ b/live/config-cdroot/fix_bootconfig.aarch64
@@ -5,6 +5,8 @@
# https://github.com/openSUSE/agama/issues/1609
# KIWI config
+set -e
+
dst="$1"
if [ ! -d "$dst" ] ; then
@@ -17,11 +19,21 @@ fi
# but that's not the case.
test -f $dst/.profile && . $dst/.profile
+arch=`uname -m`
+profile=$(echo "$kiwi_profiles" | tr "-" " ")
+label="$profile ($arch)"
+
+if [ -d "$dst/boot/grub2/themes/SLE" ]; then
+ theme="SLE"
+else
+ theme="openSUSE"
+end
+
#
# Create grub.cfg
#
cat >$dst/boot/grub2/grub.cfg <$dst/boot/grub2/grub.cfg <$dst/ppc/bootinfo.txt <
-$kiwi_iname
-openSuSE Tumbleweed
+Install $label
+$labelboot &device;:1,\boot\grub2\grub.elf
XXX
diff --git a/live/config-cdroot/fix_bootconfig.s390x b/live/config-cdroot/fix_bootconfig.s390x
index 4dc9e6dbc4..31ae0fbb43 100755
--- a/live/config-cdroot/fix_bootconfig.s390x
+++ b/live/config-cdroot/fix_bootconfig.s390x
@@ -40,6 +40,8 @@
# └── susehmc.ins
#
+set -e
+
dst="$1"
if [ ! -d "$dst" ] ; then
diff --git a/live/config-cdroot/fix_bootconfig.x86_64 b/live/config-cdroot/fix_bootconfig.x86_64
index 07e0656e6a..a81b98159a 100755
--- a/live/config-cdroot/fix_bootconfig.x86_64
+++ b/live/config-cdroot/fix_bootconfig.x86_64
@@ -5,6 +5,8 @@
# https://github.com/openSUSE/agama/issues/1609
# KIWI config
+set -e
+
dst="$1"
if [ ! -d "$dst" ] ; then
@@ -17,11 +19,21 @@ fi
# but that's not the case.
test -f $dst/.profile && . $dst/.profile
+arch=`uname -m`
+profile=$(echo "$kiwi_profiles" | tr "-" " ")
+label="$profile ($arch)"
+
+if [ -d "$dst/boot/grub2/themes/SLE" ]; then
+ theme="SLE"
+else
+ theme="openSUSE"
+fi
+
#
# Create grub.cfg
#
cat >$dst/boot/grub2/grub.cfg < $PREFS
+# read the system locale from EFI if present
+LANG_FILE=/sys/firmware/efi/efivars/PlatformLang-8be4df61-93ca-11d2-aa0d-00e098032b8c
+
+# file exists and is not empty
+if [ -s "$LANG_FILE" ]; then
+ # skip the first 4 bytes (the EFI attributes), keep only the characters allowed in a language code,
+ # especially remove the trailing null byte
+ EFI_LANG=$(cat "$LANG_FILE" | tail -c +5 | tr -cd "[_a-zA-Z-]")
+
+ if [ -n "$EFI_LANG" ]; then
+ # escape & because it has a special meaning in sed replacement
+ LANG_QUERY="\\&lang=$EFI_LANG"
+ fi
+fi
+
+sed -e "s/__HOMEPAGE__/http:\/\/localhost\/login?token=$TOKEN$LANG_QUERY/" $PREFS.template > $PREFS
+
firefox --kiosk --profile $HOME/.mozilla/firefox/profile
diff --git a/live/root/root/.mozilla/firefox/profile/user.js.template b/live/root/root/.mozilla/firefox/profile/user.js.template
index d4e5d6b343..50737023cb 100644
--- a/live/root/root/.mozilla/firefox/profile/user.js.template
+++ b/live/root/root/.mozilla/firefox/profile/user.js.template
@@ -8,6 +8,11 @@ user_pref("signon.generation.enabled", false);
// disable the initial configuration workflow
user_pref("browser.aboutwelcome.enabled", false);
+// do not ask to restore the session after restarting the browser
+// via "systemctl restart x11-autologin"
+user_pref("browser.sessionstore.resume_from_crash", false);
+user_pref("browser.startup.couldRestoreSession.count", 0);
+
// disable homepage override on updates
user_pref("browser.startup.homepage_override.mstone", "ignore");
diff --git a/live/root/tmp/driver_cleanup.rb b/live/root/tmp/driver_cleanup.rb
index f5cff78ba2..ef3b4caa61 100755
--- a/live/root/tmp/driver_cleanup.rb
+++ b/live/root/tmp/driver_cleanup.rb
@@ -127,25 +127,9 @@ def self.from_file(file)
to_delete.reject!{|a| referenced.any?{|d| d.path == a.path}}
end
-# total size counter
-driver_size = 0
+delete_drivers = to_delete.map(&:path)
+puts "Found #{delete_drivers.size} drivers to delete"
+File.delete(*delete_drivers) if do_delete
-# process the list of the drivers to delete
-to_delete.each do |d|
- driver_size += File.size(d.path)
-
- if (do_delete)
- puts "Deleting #{d.path}"
- File.delete(d.path)
- else
- puts "Driver to delete #{d.path}"
- end
-end
-
-puts "Found #{to_delete.size} drivers to delete (#{driver_size/1024/1024} MiB)"
-
-# at the end update the kernel driver metadata (modules.dep and others)
-if (do_delete)
- puts "Updating driver metadata..."
- system("/sbin/depmod -a -F #{dir.shellescape}/System.map")
-end
+# Note: The module dependencies are updated by the config.sh script
+# after decompressing the drivers.
diff --git a/live/root/tmp/fw_cleanup.rb b/live/root/tmp/fw_cleanup.rb
index 35b3782c2c..d82406e54a 100755
--- a/live/root/tmp/fw_cleanup.rb
+++ b/live/root/tmp/fw_cleanup.rb
@@ -72,6 +72,7 @@ def symlinks(path, fw_dir)
end
end
+puts "Removing unused firmware..."
# counter for total unused firmware size
unused_size = 0
@@ -84,7 +85,6 @@ def symlinks(path, fw_dir)
if !fw.include?(fw_name)
unused_size += File.size(fw_path)
if (do_delete)
- puts "Deleting firmware file #{fw_path}"
File.delete(fw_path)
else
puts "Found unused firmware #{fw_path}"
@@ -95,9 +95,9 @@ def symlinks(path, fw_dir)
# do some cleanup at the end
if (do_delete)
puts "Removing dangling symlinks..."
- system("find #{fw_dir.shellescape} -xtype l -print -delete")
+ system("find #{fw_dir.shellescape} -xtype l -delete")
puts "Removing empty directories..."
- system("find #{fw_dir.shellescape} -type d -empty -print -delete")
+ system("find #{fw_dir.shellescape} -type d -empty -delete")
end
puts "Unused firmware size: #{unused_size} (#{unused_size >> 20} MiB)"
diff --git a/live/root/usr/bin/checkmedia-service b/live/root/usr/bin/checkmedia-service
new file mode 100755
index 0000000000..277308717c
--- /dev/null
+++ b/live/root/usr/bin/checkmedia-service
@@ -0,0 +1,28 @@
+#! /bin/sh
+
+# Note: the LIVE_MEDIUM_LABEL placeholder is replaced by the real label in config.sh script
+
+# get the partition where the live ISO is mounted, the real name is set by the
+# config.sh script which gets the live partition label name from KIWI
+disk=$(blkid -L "@@LIVE_MEDIUM_LABEL@@")
+
+if [ -z "$disk" ]; then
+ echo -e "\e[31mPartition \"@@LIVE_MEDIUM_LABEL@@\" not found, skipping media check\e[0m"
+ read -n1 -s -p "Press any key to continue... "
+ echo
+ exit 0
+fi
+
+echo "Checking data integrity of device $disk (@@LIVE_MEDIUM_LABEL@@)..."
+
+if checkmedia -v "$disk"; then
+ echo -e "\e[32mMedium check succeeded\e[0m"
+ read -n1 -s -p "Press any key to continue... "
+ echo
+else
+ echo -e "\e[31mERROR: Medium check failed!\e[0m"
+ echo "The installation medium is broken, it should not be used for installation."
+ read -n1 -s -p "Press any key to halt the system... "
+ echo
+ halt
+fi
diff --git a/live/src/agama-installer.changes b/live/src/agama-installer.changes
index b3380f4586..b8ff62e67d 100644
--- a/live/src/agama-installer.changes
+++ b/live/src/agama-installer.changes
@@ -1,3 +1,76 @@
+-------------------------------------------------------------------
+Wed Feb 12 12:02:39 UTC 2025 - Ladislav Slezák
+
+- Do not print details about removed kernel drivers and firmware
+ files during build, this speeds up the build significantly
+ (~1 minute faster build) and avoids huge build log.
+- Uncompress the kernel drivers, no need to compress them twice
+ (they are compressed by the squashfs as well). Compressing
+ all drivers together in the image is more effective than
+ compressing several thousands individual files.
+ This makes the image about 33MB smaller (on x86_64). (boo#1192457)
+- Hardlink the duplicate licenses, makes the ISO ~1MB smaller
+
+-------------------------------------------------------------------
+Wed Feb 12 10:17:47 UTC 2025 - Giacomo Leidi
+
+- Add ISO publisher (gh#agama-project/agama#1967)
+
+-------------------------------------------------------------------
+Wed Feb 12 08:04:11 UTC 2025 - Ladislav Slezák
+
+- Start root shell in a free terminal (usually tty2)
+- Fixed restarting the x11-autologin service
+- Do not ask to restore the browser session after restarting it
+- Print only kernel errors or more severe messages on the console,
+ avoid spamming the terminal with useless texts (bsc#1237056)
+
+-------------------------------------------------------------------
+Fri Feb 7 16:31:50 UTC 2025 - Eugenio Paolantonio
+
+- live: fix_bootconfig.s390x: restore initrd.off naming for the initrd offset
+ This essentially reverts the previous change (bsc#1236781, gh#agama-project/agama#1969,
+ gh#agama-project/agama#1974)
+
+-------------------------------------------------------------------
+Fri Feb 7 09:15:02 UTC 2025 - Ladislav Slezák
+
+- In local installation prefer the language configured in the
+ UEFI firmware
+
+-------------------------------------------------------------------
+Fri Feb 7 08:21:29 UTC 2025 - Ladislav Slezák
+
+- Implemented media check functionality (bsc#1236103)
+
+-------------------------------------------------------------------
+Fri Feb 7 08:10:46 UTC 2025 - Ladislav Slezák
+
+- Use better ISO Volume ID labels (bsc#1236401)
+- Improve also the boot menu labels
+- Use the graphical boot menu also in SLE
+- Added UEFI firmware settings boot menu option
+
+-------------------------------------------------------------------
+Tue Feb 4 13:25:35 UTC 2025 - Ladislav Slezák
+
+- configure xterm to use the default fixed font also in the
+ configuration popup menu (Ctrl + click) to avoid crash
+- bsc#1235478
+ - tuned IceWM setup to disable some desktop functionality like
+ opening main menu using the Win key (by mfilka@suse.com)
+
+-------------------------------------------------------------------
+Mon Feb 3 23:08:34 UTC 2025 - Eugenio Paolantonio
+
+- live: fix_bootconfig.s390x: use initrd.ofs for the initrd
+ offset filename (gh#agama-project/agama#1969)
+
+-------------------------------------------------------------------
+Mon Feb 3 23:01:37 UTC 2025 - Eugenio Paolantonio
+
+- fix_bootimage: exit on failures (gh#agama-project/agama#1969)
+
-------------------------------------------------------------------
Mon Jan 20 10:37:43 UTC 2025 - Imobach Gonzalez Sosa
diff --git a/live/src/agama-installer.kiwi b/live/src/agama-installer.kiwi
index 4350da69b2..2d64f666bd 100644
--- a/live/src/agama-installer.kiwi
+++ b/live/src/agama-installer.kiwi
@@ -9,11 +9,14 @@
Agama Live ISO
-
+
+
+
-
-
-
+
+
+
+ 11.0.0
@@ -26,24 +29,25 @@
bgrtopenSUSE
-
-
+
+
+
-
-
+
+
-
-
+
+
-
+
-
+ true
@@ -55,8 +59,8 @@
3000
-
-
+
+ true
@@ -68,8 +72,8 @@
1900
-
-
+
+ true
@@ -88,7 +92,7 @@
-
+
@@ -162,7 +166,7 @@
-
+
@@ -182,7 +186,7 @@
-
+
@@ -197,14 +201,14 @@
-
+
-
+
diff --git a/live/src/config.sh b/live/src/config.sh
index 607fe25094..d51b0ccf14 100644
--- a/live/src/config.sh
+++ b/live/src/config.sh
@@ -55,6 +55,8 @@ systemctl enable live-password-dialog.service
systemctl enable live-password-iso.service
systemctl enable live-password-random.service
systemctl enable live-password-systemd.service
+systemctl enable live-root-shell.service
+systemctl enable checkmedia.service
systemctl enable setup-systemd-proxy-env.path
systemctl enable x11-autologin.service
systemctl enable spice-vdagentd.service
@@ -72,8 +74,9 @@ systemctl disable YaST2-Firstboot.service
systemctl disable YaST2-Second-Stage.service
### setup dracut for live system
-label=${kiwi_install_volid:-$kiwi_iname}
arch=$(uname -m)
+# keep in sync with ISO Volume ID set in the fix_bootconfig script
+label="Install-$kiwi_profiles-$arch"
echo "Setting default live root: live:LABEL=$label"
mkdir /etc/cmdline.d
@@ -91,6 +94,7 @@ fi
# replace the @@LIVE_MEDIUM_LABEL@@ with the real Live partition label name from KIWI
sed -i -e "s/@@LIVE_MEDIUM_LABEL@@/$label/g" /usr/bin/live-password
+sed -i -e "s/@@LIVE_MEDIUM_LABEL@@/$label/g" /usr/bin/checkmedia-service
# Increase the Live ISO image size to have some extra free space for installing
# additional debugging or development packages.
@@ -175,9 +179,19 @@ du -h -s /lib/modules /lib/firmware
################################################################################
# The rest of the file was copied from the openSUSE Tumbleweed Live ISO
-# https://build.opensuse.org/package/view_file/openSUSE:Factory:Live/livecd-tumbleweed-kde/config.sh?expand=1
+# https://build.opensuse.org/projects/openSUSE:Factory:Live/packages/livecd-tumbleweed-kde/files/config.sh?expand=1
#
+# Decompress kernel modules, better for squashfs (boo#1192457)
+find /lib/modules/*/kernel -name '*.ko.xz' -exec xz -d {} +
+find /lib/modules/*/kernel -name '*.ko.zst' -exec zstd --rm -d {} +
+for moddir in /lib/modules/*; do
+ depmod "$(basename "$moddir")"
+done
+
+# Reuse what the macro does
+rpm --eval "%fdupes /usr/share/licenses" | sh
+
# disable the services included by dependencies
for s in purge-kernels; do
systemctl -f disable $s || true
diff --git a/live/src/fix_bootconfig b/live/src/fix_bootconfig
index 26357f7c25..77ec7b6416 100644
--- a/live/src/fix_bootconfig
+++ b/live/src/fix_bootconfig
@@ -15,7 +15,6 @@
#
set -ex
-
dst=${1#iso:}
# KIWI config
@@ -27,11 +26,6 @@ fi
arch=`uname -m`
-# removing live root setting from command line - it's in /etc/cmdline.d instead
-if [ -f $dst/boot/grub2/grub.cfg ] ; then
- sed -i -E -e 's/\s+root=\S+//' -e 's/\s+rd.live\.image//' $dst/boot/grub2/grub.cfg
-fi
-
bootfix=$dst/fix_bootconfig.$arch
if [ -f $bootfix ] ; then
echo "bootconfig script found: \"$bootfix\""
@@ -55,22 +49,14 @@ for i in "\$@" ; do
export iso="\$i"
continue
fi
- if [ -n "\$volid_opt" ] ; then
- volid_opt=
- export volid="\$i"
- continue
- fi
if [ "\$i" = "-outdev" ] ; then
iso_opt=1
continue
fi
- if [ "\$i" = "-volid" ] ; then
- volid_opt=1
- continue
- fi
done
-volid=${kiwi_install_volid:-$kiwi_iname}
+# keep in sync with ISO Volume ID set in the config.sh script
+volid="Install-$kiwi_profiles-$arch"
[ -x $bootfix ] && $bootfix $dst
rm -f $dst/fix_bootconfig.* $dst/.profile
@@ -87,6 +73,7 @@ case $arch in
;;
*)
/usr/bin/xorriso "\$@" -volid "\$volid"
+ err=\$?
esac
exit \$err
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index d14e624000..3762917113 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -56,10 +56,12 @@ dependencies = [
"jsonschema",
"jsonwebtoken",
"log",
+ "regex",
"reqwest 0.12.8",
"serde",
"serde_json",
"serde_repr",
+ "serde_with",
"strum",
"tempfile",
"thiserror",
@@ -3078,9 +3080,9 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.11.0"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
@@ -3472,9 +3474,9 @@ dependencies = [
[[package]]
name = "serde_with"
-version = "3.10.0"
+version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9720086b3357bcb44fce40117d769a4d068c70ecfa190850a980a71755f66fcc"
+checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -3490,9 +3492,9 @@ dependencies = [
[[package]]
name = "serde_with_macros"
-version = "3.10.0"
+version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f1abbfe725f27678f4663bcacb75a83e829fd464c25d78dd038a3a29e307cec"
+checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
dependencies = [
"darling",
"proc-macro2",
diff --git a/rust/agama-lib/Cargo.toml b/rust/agama-lib/Cargo.toml
index 7b3dfd9f6c..ac545fb0ba 100644
--- a/rust/agama-lib/Cargo.toml
+++ b/rust/agama-lib/Cargo.toml
@@ -35,6 +35,8 @@ chrono = { version = "0.4.38", default-features = false, features = [
home = "0.5.9"
strum = { version = "0.26.3", features = ["derive"] }
fs_extra = "1.3.0"
+serde_with = "3.12.0"
+regex = "1.11.1"
[dev-dependencies]
httpmock = "0.7.0"
diff --git a/rust/agama-lib/src/profile.rs b/rust/agama-lib/src/profile.rs
index 4396d42e7d..23e24b9f18 100644
--- a/rust/agama-lib/src/profile.rs
+++ b/rust/agama-lib/src/profile.rs
@@ -51,7 +51,8 @@ impl AutoyastProfileImporter {
let tmp_dir = TempDir::with_prefix(TMP_DIR_PREFIX)?;
Command::new("agama-autoyast")
.args([url.as_str(), &tmp_dir.path().to_string_lossy()])
- .status()?;
+ .status()
+ .context("Failed to run agama-autoyast")?;
let autoinst_json = tmp_dir.path().join(AUTOINST_JSON);
let content = fs::read_to_string(autoinst_json)?;
diff --git a/rust/agama-lib/src/software/model.rs b/rust/agama-lib/src/software/model.rs
index bb290803fd..0bca26c619 100644
--- a/rust/agama-lib/src/software/model.rs
+++ b/rust/agama-lib/src/software/model.rs
@@ -1,4 +1,4 @@
-// Copyright (c) [2024] SUSE LLC
+// Copyright (c) [2025] SUSE LLC
//
// All Rights Reserved.
//
@@ -18,103 +18,10 @@
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.
-use serde::{Deserialize, Serialize};
-use std::collections::HashMap;
+mod license;
+mod packages;
+mod registration;
-/// Software service configuration (product, patterns, etc.).
-#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
-pub struct SoftwareConfig {
- /// A map where the keys are the pattern names and the values whether to install them or not.
- pub patterns: Option>,
- /// Name of the product to install.
- pub product: Option,
-}
-
-/// Software service configuration (product, patterns, etc.).
-#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
-pub struct RegistrationParams {
- /// Registration key.
- pub key: String,
- /// Registration email.
- pub email: String,
-}
-
-/// Information about registration configuration (product, patterns, etc.).
-#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct RegistrationInfo {
- /// Registration key. Empty value mean key not used or not registered.
- pub key: String,
- /// Registration email. Empty value mean email not used or not registered.
- pub email: String,
-}
-
-#[derive(
- Clone,
- Default,
- Debug,
- Serialize,
- Deserialize,
- strum::Display,
- strum::EnumString,
- utoipa::ToSchema,
-)]
-#[strum(serialize_all = "camelCase")]
-#[serde(rename_all = "camelCase")]
-pub enum RegistrationRequirement {
- /// Product does not require registration
- #[default]
- No = 0,
- /// Product has optional registration
- Optional = 1,
- /// It is mandatory to register the product
- Mandatory = 2,
-}
-
-#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
-pub struct RegistrationError {
- /// ID of error. See dbus API for possible values
- pub id: u32,
- /// human readable error string intended to be displayed to user
- pub message: String,
-}
-
-/// Software resolvable type (package or pattern).
-#[derive(Deserialize, Serialize, strum::Display, utoipa::ToSchema)]
-#[strum(serialize_all = "camelCase")]
-#[serde(rename_all = "camelCase")]
-pub enum ResolvableType {
- Package = 0,
- Pattern = 1,
-}
-
-/// Resolvable list specification.
-#[derive(Deserialize, Serialize, utoipa::ToSchema)]
-pub struct ResolvableParams {
- /// List of resolvables.
- pub names: Vec,
- /// Resolvable type.
- pub r#type: ResolvableType,
- /// Whether the resolvables are optional or not.
- pub optional: bool,
-}
-
-/// Repository list specification.
-#[derive(Deserialize, Serialize, utoipa::ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct Repository {
- /// repository identifier
- pub id: i32,
- /// repository alias. Has to be unique
- pub alias: String,
- /// repository name
- pub name: String,
- /// Repository url (raw format without expanded variables)
- pub url: String,
- /// product directory (currently not used, valid only for multiproduct DVDs)
- pub product_dir: String,
- /// Whether the repository is enabled
- pub enabled: bool,
- /// Whether the repository is loaded
- pub loaded: bool,
-}
+pub use license::*;
+pub use packages::*;
+pub use registration::*;
diff --git a/rust/agama-server/src/software/license.rs b/rust/agama-lib/src/software/model/license.rs
similarity index 93%
rename from rust/agama-server/src/software/license.rs
rename to rust/agama-lib/src/software/model/license.rs
index 1d769a4f7d..f33d6c07a8 100644
--- a/rust/agama-server/src/software/license.rs
+++ b/rust/agama-lib/src/software/model/license.rs
@@ -1,4 +1,4 @@
-// Copyright (c) [2024] SUSE LLC
+// Copyright (c) [2024-2025] SUSE LLC
//
// All Rights Reserved.
//
@@ -34,9 +34,11 @@ use thiserror::Error;
///
/// It contains the license ID and the list of languages that with a translation.
#[serde_as]
-#[derive(Clone, Debug, Serialize)]
+#[derive(Clone, Debug, Serialize, utoipa::ToSchema)]
pub struct License {
+ /// License ID.
pub id: String,
+ /// Languages in which the license is translated.
#[serde_as(as = "Vec")]
pub languages: Vec,
}
@@ -46,9 +48,11 @@ pub struct License {
/// It contains the license ID and the body.
///
/// TODO: in the future it might contain a title, extracted from the text.
-#[derive(Clone, Debug, Serialize)]
+#[derive(Clone, Debug, Serialize, utoipa::ToSchema)]
pub struct LicenseContent {
+ /// License ID.
pub id: String,
+ /// License text.
pub body: String,
}
@@ -106,13 +110,13 @@ impl LicensesRepo {
}
candidates.push(format!("license.{}.txt", language.language));
candidates.push("license.txt".to_string());
- tracing::info!("Searching for license: {:?}", &candidates);
+ log::info!("Searching for license: {:?}", &candidates);
let license_path = candidates
.into_iter()
.map(|p| self.path.join(id).join(p))
.find(|p| p.exists())?;
- tracing::info!("Reading license from {}", &license_path.display());
+ log::info!("Reading license from {}", &license_path.display());
let body: String = std::fs::read_to_string(license_path).ok()?;
@@ -146,7 +150,7 @@ impl LicensesRepo {
/// The language is inferred from the file name (e.g., "es-ES" for license.es_ES.txt").
fn language_tag_from_file(name: &str) -> Option {
if !name.starts_with("license") {
- tracing::warn!("Unexpected file in the licenses directory: {}", &name);
+ log::warn!("Unexpected file in the licenses directory: {}", &name);
return None;
}
let mut parts = name.split(".");
@@ -175,7 +179,7 @@ impl Default for LicensesRepo {
/// Simplified representation of the RFC 5646 language code.
///
/// It only considers xx and xx-XX formats.
-#[derive(Clone, Debug, Serialize, PartialEq)]
+#[derive(Clone, Debug, Serialize, PartialEq, utoipa::ToSchema)]
pub struct LanguageTag {
// ISO-639
pub language: String,
@@ -225,8 +229,7 @@ impl TryFrom<&str> for LanguageTag {
#[cfg(test)]
mod test {
- use super::LicensesRepo;
- use crate::software::license::LanguageTag;
+ use super::{LanguageTag, LicensesRepo};
use std::path::Path;
fn build_repo() -> LicensesRepo {
diff --git a/rust/agama-lib/src/software/model/packages.rs b/rust/agama-lib/src/software/model/packages.rs
new file mode 100644
index 0000000000..f402c292a2
--- /dev/null
+++ b/rust/agama-lib/src/software/model/packages.rs
@@ -0,0 +1,71 @@
+// Copyright (c) [2025] SUSE LLC
+//
+// All Rights Reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 of the License, or (at your option)
+// any later version.
+//
+// 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.
+
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+
+/// Software service configuration (product, patterns, etc.).
+#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
+pub struct SoftwareConfig {
+ /// A map where the keys are the pattern names and the values whether to install them or not.
+ pub patterns: Option>,
+ /// Name of the product to install.
+ pub product: Option,
+}
+
+/// Software resolvable type (package or pattern).
+#[derive(Deserialize, Serialize, strum::Display, utoipa::ToSchema)]
+#[strum(serialize_all = "camelCase")]
+#[serde(rename_all = "camelCase")]
+pub enum ResolvableType {
+ Package = 0,
+ Pattern = 1,
+}
+
+/// Resolvable list specification.
+#[derive(Deserialize, Serialize, utoipa::ToSchema)]
+pub struct ResolvableParams {
+ /// List of resolvables.
+ pub names: Vec,
+ /// Resolvable type.
+ pub r#type: ResolvableType,
+ /// Whether the resolvables are optional or not.
+ pub optional: bool,
+}
+
+/// Repository list specification.
+#[derive(Deserialize, Serialize, utoipa::ToSchema)]
+#[serde(rename_all = "camelCase")]
+pub struct Repository {
+ /// repository identifier
+ pub id: i32,
+ /// repository alias. Has to be unique
+ pub alias: String,
+ /// repository name
+ pub name: String,
+ /// Repository url (raw format without expanded variables)
+ pub url: String,
+ /// product directory (currently not used, valid only for multiproduct DVDs)
+ pub product_dir: String,
+ /// Whether the repository is enabled
+ pub enabled: bool,
+ /// Whether the repository is loaded
+ pub loaded: bool,
+}
diff --git a/rust/agama-lib/src/software/model/registration.rs b/rust/agama-lib/src/software/model/registration.rs
new file mode 100644
index 0000000000..8f2e143b63
--- /dev/null
+++ b/rust/agama-lib/src/software/model/registration.rs
@@ -0,0 +1,70 @@
+// Copyright (c) [2025] SUSE LLC
+//
+// All Rights Reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 of the License, or (at your option)
+// any later version.
+//
+// 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.
+
+use serde::{Deserialize, Serialize};
+
+/// Software service configuration (product, patterns, etc.).
+#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
+pub struct RegistrationParams {
+ /// Registration key.
+ pub key: String,
+ /// Registration email.
+ pub email: String,
+}
+
+/// Information about registration configuration (product, patterns, etc.).
+#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
+#[serde(rename_all = "camelCase")]
+pub struct RegistrationInfo {
+ /// Registration key. Empty value mean key not used or not registered.
+ pub key: String,
+ /// Registration email. Empty value mean email not used or not registered.
+ pub email: String,
+}
+
+#[derive(
+ Clone,
+ Default,
+ Debug,
+ Serialize,
+ Deserialize,
+ strum::Display,
+ strum::EnumString,
+ utoipa::ToSchema,
+)]
+#[strum(serialize_all = "camelCase")]
+#[serde(rename_all = "camelCase")]
+pub enum RegistrationRequirement {
+ /// Product does not require registration
+ #[default]
+ No = 0,
+ /// Product has optional registration
+ Optional = 1,
+ /// It is mandatory to register the product
+ Mandatory = 2,
+}
+
+#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
+pub struct RegistrationError {
+ /// ID of error. See dbus API for possible values
+ pub id: u32,
+ /// human readable error string intended to be displayed to user
+ pub message: String,
+}
diff --git a/rust/agama-server/src/software.rs b/rust/agama-server/src/software.rs
index 6bdfb1e0d1..b363de6ad1 100644
--- a/rust/agama-server/src/software.rs
+++ b/rust/agama-server/src/software.rs
@@ -20,4 +20,3 @@
pub mod web;
pub use web::{software_service, software_streams};
-mod license;
diff --git a/rust/agama-server/src/software/web.rs b/rust/agama-server/src/software/web.rs
index 3bbd61b2d5..92d94e4bf2 100644
--- a/rust/agama-server/src/software/web.rs
+++ b/rust/agama-server/src/software/web.rs
@@ -27,7 +27,6 @@
use crate::{
error::Error,
- software::license::LicensesRepo,
web::{
common::{issues_router, progress_router, service_status_router, EventStreams},
Event,
@@ -39,8 +38,8 @@ use agama_lib::{
product::{proxies::RegistrationProxy, Product, ProductClient},
software::{
model::{
- RegistrationError, RegistrationInfo, RegistrationParams, Repository, ResolvableParams,
- SoftwareConfig,
+ License, LicenseContent, LicensesRepo, RegistrationError, RegistrationInfo,
+ RegistrationParams, Repository, ResolvableParams, SoftwareConfig,
},
proxies::{Software1Proxy, SoftwareProductProxy},
Pattern, SelectedBy, SoftwareClient, UnknownSelectedBy,
@@ -57,8 +56,6 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tokio_stream::{Stream, StreamExt};
-use super::license::License;
-
#[derive(Clone)]
struct SoftwareState<'a> {
product: ProductClient<'a>,
@@ -501,7 +498,7 @@ async fn set_resolvables(
path = "/licenses",
context_path = "/api/software",
responses(
- (status = 200, description = "List of known licenses")
+ (status = 200, description = "List of known licenses", body = Vec)
)
)]
async fn licenses(State(state): State>) -> Result>, Error> {
@@ -521,8 +518,9 @@ struct LicenseQuery {
get,
path = "/licenses/:id",
context_path = "/api/software",
+ params(LicenseQuery),
responses(
- (status = 200, description = "License with the given ID"),
+ (status = 200, description = "License with the given ID", body = LicenseContent),
(status = 400, description = "The specified language tag is not valid"),
(status = 404, description = "There is not license with the given ID")
)
diff --git a/rust/agama-server/src/web/docs/software.rs b/rust/agama-server/src/web/docs/software.rs
index e6155cffd0..0062b56295 100644
--- a/rust/agama-server/src/web/docs/software.rs
+++ b/rust/agama-server/src/web/docs/software.rs
@@ -37,12 +37,14 @@ impl ApiDocBuilder for SoftwareApiDocBuilder {
.path_from::()
.path_from::()
.path_from::()
+ .path_from::()
+ .path_from::()
.path_from::()
.path_from::()
.path_from::()
.path_from::()
- .path_from::()
.path_from::()
+ .path_from::()
.path_from::()
.path_from::()
.build()
@@ -53,14 +55,17 @@ impl ApiDocBuilder for SoftwareApiDocBuilder {
.schema_from::()
.schema_from::()
.schema_from::()
+ .schema_from::()
+ .schema_from::()
+ .schema_from::()
+ .schema_from::()
+ .schema_from::()
.schema_from::()
.schema_from::()
- .schema_from::()
+ .schema_from::()
.schema_from::()
.schema_from::()
- .schema_from::()
.schema_from::()
- .schema_from::()
.schema_from::()
.schema_from::()
.build()
diff --git a/rust/agama-server/src/web/http.rs b/rust/agama-server/src/web/http.rs
index 3cc84054a7..63267051f5 100644
--- a/rust/agama-server/src/web/http.rs
+++ b/rust/agama-server/src/web/http.rs
@@ -100,10 +100,12 @@ pub async fn login(
pub struct LoginFromQueryParams {
/// Token to use for authentication.
token: String,
+ /// Optional requested locale
+ lang: Option,
}
#[utoipa::path(get, path = "/login", responses(
- (status = 301, description = "Injects the authentication cookie if correct and redirects to the web UI")
+ (status = 307, description = "Injects the authentication cookie if correct and redirects to the web UI")
))]
pub async fn login_from_query(
State(state): State,
@@ -120,7 +122,23 @@ pub async fn login_from_query(
);
}
- headers.insert(header::LOCATION, HeaderValue::from_static("/"));
+ // the redirection path
+ let mut target = String::from("/");
+
+ // keep the "lang" URL query if it was present in the original request
+ if let Some(lang) = params.lang {
+ if !lang.is_empty() {
+ target.push_str(format!("?lang={}", lang).as_str());
+ }
+ }
+
+ let location = HeaderValue::from_str(target.as_str());
+
+ headers.insert(
+ header::LOCATION,
+ location.unwrap_or(HeaderValue::from_static("/")),
+ );
+
(StatusCode::TEMPORARY_REDIRECT, headers)
}
diff --git a/rust/package/agama.changes b/rust/package/agama.changes
index df3eec7934..bfcdba01c4 100644
--- a/rust/package/agama.changes
+++ b/rust/package/agama.changes
@@ -1,3 +1,22 @@
+-------------------------------------------------------------------
+Mon Feb 10 13:28:34 UTC 2025 - Ladislav Slezák
+
+- Fixup: Make the "lang" URL query optional, do not fail when it
+ is missing. This fixes crash on non-UEFI systems.
+
+-------------------------------------------------------------------
+Fri Feb 7 11:03:29 UTC 2025 - Ladislav Slezák
+
+- Forward the "lang" URL query parameter when redirecting in the
+ "/login" endpoint (this allows to define the default language
+ in the Firefox configuration file in local installation)
+
+-------------------------------------------------------------------
+Thu Feb 6 12:52:11 UTC 2025 - Imobach Gonzalez Sosa
+
+- Describe licenses API in OpenAPI documentation
+ (gh#agama-project/agama#1929).
+
-------------------------------------------------------------------
Fri Jan 24 09:33:31 UTC 2025 - Imobach Gonzalez Sosa
diff --git a/service/bin/agama-autoyast b/service/bin/agama-autoyast
index 1df1e6eaed..a28e5857ae 100755
--- a/service/bin/agama-autoyast
+++ b/service/bin/agama-autoyast
@@ -28,12 +28,16 @@ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
# Set the PATH to a known value
ENV["PATH"] = "/sbin:/usr/sbin:/usr/bin:/bin"
+# Disable AutoYaST XML validation. It will be enabled in the future.
+ENV["YAST_SKIP_XML_VALIDATION"] = "1"
+
require "rubygems"
# find Gemfile when D-Bus activates a git checkout
Dir.chdir(__dir__) do
require "bundler/setup"
end
-require "agama/autoyast/converter"
+
+require "agama/commands/agama_autoyast"
if ARGV.length != 2
warn "Usage: #{$PROGRAM_NAME} URL DIRECTORY"
@@ -42,9 +46,15 @@ end
begin
url, directory = ARGV
- converter = Agama::AutoYaST::Converter.new(url)
- converter.to_agama(directory)
-rescue RuntimeError => e
- warn "Could not load the profile from #{url}: #{e}"
- exit 2
+ result = Agama::Commands::AgamaAutoYaST.new(url, directory).run
+ if !result
+ warn "Did not convert the profile (canceled by the user)."
+ exit 2
+ end
+rescue Agama::Commands::CouldNotFetchProfile
+ warn "Could not fetch the AutoYaST profile."
+ exit 3
+rescue Agama::Commands::CouldNotWriteAgamaConfig
+ warn "Could not write the Agama configuration."
+ exit 4
end
diff --git a/service/lib/agama/autoyast/bond_reader.rb b/service/lib/agama/autoyast/bond_reader.rb
index 3bb4669a9f..edeaf59746 100755
--- a/service/lib/agama/autoyast/bond_reader.rb
+++ b/service/lib/agama/autoyast/bond_reader.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/env ruby
# frozen_string_literal: true
# Copyright (c) [2024] SUSE LLC
diff --git a/service/lib/agama/autoyast/connections_reader.rb b/service/lib/agama/autoyast/connections_reader.rb
index 4dfb615ed1..bcdd4ae62e 100755
--- a/service/lib/agama/autoyast/connections_reader.rb
+++ b/service/lib/agama/autoyast/connections_reader.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/env ruby
# frozen_string_literal: true
# Copyright (c) [2024] SUSE LLC
diff --git a/service/lib/agama/autoyast/converter.rb b/service/lib/agama/autoyast/converter.rb
index b2cad924e6..f1e95c7eea 100755
--- a/service/lib/agama/autoyast/converter.rb
+++ b/service/lib/agama/autoyast/converter.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/env ruby
# frozen_string_literal: true
# Copyright (c) [2024] SUSE LLC
@@ -50,73 +49,6 @@ module AutoYaST
# TODO: handle invalid profiles (YAST_SKIP_XML_VALIDATION).
# TODO: capture reported errors (e.g., via the Report.Error function).
class Converter
- # @param profile_url [String] Profile URL
- def initialize(profile_url)
- @profile_url = profile_url
- end
-
- # Converts the profile into a set of files that Agama can process.
- #
- # @param dir [Pathname,String] Directory to write the profile.
- def to_agama(dir)
- path = Pathname(dir)
- FileUtils.mkdir_p(path)
- import_yast
- profile = read_profile
- File.write(path.join("autoinst.json"), export_profile(profile).to_json)
- end
-
- private
-
- attr_reader :profile_url
-
- def copy_profile; end
-
- # @return [Hash] AutoYaST profile
- def read_profile
- FileUtils.mkdir_p(Yast::AutoinstConfig.profile_dir)
-
- # fetch the profile
- Yast::AutoinstConfig.ParseCmdLine(profile_url)
- Yast::ProfileLocation.Process
-
- # put the profile in the tmp directory
- FileUtils.cp(
- Yast::AutoinstConfig.xml_tmpfile,
- tmp_profile_path
- )
-
- loop do
- Yast::Profile.ReadXML(tmp_profile_path)
- run_pre_scripts
- break unless File.exist?(Yast::AutoinstConfig.modified_profile)
-
- FileUtils.cp(Yast::AutoinstConfig.modified_profile, tmp_profile_path)
- FileUtils.rm(Yast::AutoinstConfig.modified_profile)
- end
-
- Yast::Profile.current
- end
-
- def run_pre_scripts
- pre_scripts = Yast::Profile.current.fetch_as_hash("scripts")
- .fetch_as_array("pre-scripts")
- .map { |h| Y2Autoinstallation::PreScript.new(h) }
- script_runner = Y2Autoinstall::ScriptRunner.new
-
- pre_scripts.each do |script|
- script.create_script_file
- script_runner.run(script)
- end
- end
-
- def tmp_profile_path
- @tmp_profile_path ||= File.join(
- Yast::AutoinstConfig.profile_dir,
- "autoinst.xml"
- )
- end
-
# Sections which have a corresponding reader. The reader is expected to be
# named in Pascal case and adding "Reader" as suffix (e.g., "L10nReader").
SECTIONS = ["l10n", "product", "root", "scripts", "software", "storage", "user"].freeze
@@ -126,7 +58,7 @@ def tmp_profile_path
# It goes through the list of READERS and merges the results of all of them.
#
# @return [Hash] Agama profile
- def export_profile(profile)
+ def to_agama(profile)
SECTIONS.reduce({}) do |result, section|
require "agama/autoyast/#{section}_reader"
klass = "#{section}_reader".split("_").map(&:capitalize).join
@@ -134,13 +66,6 @@ def export_profile(profile)
result.merge(reader.read)
end
end
-
- def import_yast
- Yast.import "AutoinstConfig"
- Yast.import "AutoinstScripts"
- Yast.import "Profile"
- Yast.import "ProfileLocation"
- end
end
end
end
diff --git a/service/lib/agama/autoyast/l10n_reader.rb b/service/lib/agama/autoyast/l10n_reader.rb
index 771db35cdf..80f232b0b6 100755
--- a/service/lib/agama/autoyast/l10n_reader.rb
+++ b/service/lib/agama/autoyast/l10n_reader.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/env ruby
# frozen_string_literal: true
# Copyright (c) [2024] SUSE LLC
diff --git a/service/lib/agama/autoyast/network_reader.rb b/service/lib/agama/autoyast/network_reader.rb
index 04756e4026..4aae25d5cc 100755
--- a/service/lib/agama/autoyast/network_reader.rb
+++ b/service/lib/agama/autoyast/network_reader.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/env ruby
# frozen_string_literal: true
# Copyright (c) [2024] SUSE LLC
diff --git a/service/lib/agama/autoyast/product_reader.rb b/service/lib/agama/autoyast/product_reader.rb
index 06a9de3394..96ddcccb09 100755
--- a/service/lib/agama/autoyast/product_reader.rb
+++ b/service/lib/agama/autoyast/product_reader.rb
@@ -1,4 +1,3 @@
-#!/usr/bin/env ruby
# frozen_string_literal: true
# Copyright (c) [2024] SUSE LLC
diff --git a/service/lib/agama/autoyast/profile_checker.rb b/service/lib/agama/autoyast/profile_checker.rb
new file mode 100644
index 0000000000..a4d87324a8
--- /dev/null
+++ b/service/lib/agama/autoyast/profile_checker.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2025] 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 "agama/autoyast/profile_description"
+
+module Agama
+ module AutoYaST
+ # This class checks an AutoYaST profile and determines which unsupported elements are used.
+ #
+ # It does not report unknown elements.
+ class ProfileChecker
+ # Finds unsupported profile elements.
+ #
+ # @param profile [Yast::ProfileHash] AutoYaST profile to check
+ # @return [Array] List of unsupported elements
+ def find_unsupported(profile)
+ description = ProfileDescription.load
+ elements = elements_from(profile)
+
+ elements.map do |e|
+ normalized_key = e.gsub(/\[\d+\]/, "[]")
+ element = description.find_element(normalized_key)
+ element unless element&.supported?
+ end.compact
+ end
+
+ private
+
+ # Returns the elements from the profile
+ #
+ # @return [Array] List of element IDs (e.g., "networking/backend")
+ def elements_from(profile, parent = "")
+ return [] unless profile.is_a?(Hash)
+
+ profile.map do |k, v|
+ current = parent.empty? ? k : "#{parent}#{ProfileDescription::SEPARATOR}#{k}"
+
+ children = if v.is_a?(Array)
+ v.map.with_index do |e, i|
+ elements_from(e, "#{parent}#{ProfileDescription::SEPARATOR}#{k}[#{i}]")
+ end
+ else
+ elements_from(v, k)
+ end
+
+ [current, *children]
+ end.flatten
+ end
+ end
+ end
+end
diff --git a/service/lib/agama/autoyast/profile_description.rb b/service/lib/agama/autoyast/profile_description.rb
new file mode 100644
index 0000000000..cf1ce8e459
--- /dev/null
+++ b/service/lib/agama/autoyast/profile_description.rb
@@ -0,0 +1,156 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2024] 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 "json"
+
+module Agama
+ module AutoYaST
+ # Describes an AutoYaST element and its support level in Agama
+ class ProfileElement
+ # @return [String] Element key.
+ attr_reader :key
+ # @return [String] Additional information about the element.
+ attr_reader :notes
+ # @return [String] Agama equivalent attribute
+ attr_reader :agama
+ # @return [Array] Children elements.
+ attr_reader :children
+
+ class << self
+ # Builds a ProfileElement from its JSON description.
+ def from_json(json, parent = nil)
+ support = json.key?("children") ? :yes : json["support"]&.to_sym
+ key = parent ? "#{parent}#{ProfileDescription::SEPARATOR}#{json["key"]}" : json["key"]
+ children = json.fetch("children", []).map do |json_element|
+ ProfileElement.from_json(json_element, key)
+ end
+ ProfileElement.new(key, support, json["agama"], json["notes"], children)
+ end
+ end
+
+ # Constructor
+ #
+ # @param key [String] Element key.
+ # @param support [String, nil] Support level.
+ # @param notes [String] Additional information about the element.
+ # @param agama [String] Agama equivalent attribute
+ # @param children [Array] Children elements.
+ def initialize(key, support, agama, notes, children = [])
+ @key = key
+ @support = support
+ @notes = notes
+ @agama = agama
+ @children = children
+ end
+
+ # Whether the element is supported.
+ #
+ # @return [Boolean]
+ def supported?
+ support == :yes
+ end
+
+ # Returns the support level.
+ #
+ # If it was not specified when building the object, it infers it from its children. If the
+ # element has no children, it returns :no.
+ def support
+ return @support if @support
+
+ nested = children.map(&:support).uniq
+ return nested.first if nested.size == 1
+ return :partial if nested.include?(:yes) || nested.include?(:partial)
+ return :planned if nested.include?(:planned)
+
+ :no
+ end
+
+ # Whether it is a top level element.
+ #
+ # @return [Boolean]
+ def top_level?
+ !key.include?(ProfileDescription::SEPARATOR)
+ end
+
+ # Short key name.
+ #
+ # @return [String]
+ def short_key
+ key.split(ProfileDescription::SEPARATOR).last
+ end
+ end
+
+ # Describes the AutoYaST profile format.
+ class ProfileDescription
+ SEPARATOR = "/"
+
+ # @return [Array]
+ attr_reader :elements
+
+ DEFAULT_PATH = File.expand_path("#{__dir__}/../../../share/autoyast-compat.json")
+
+ class << self
+ # Load the AutoYaST profile definition.
+ #
+ # @param path [String] Path of the profile definition.
+ # @return [ProfileDescription]
+ def load(path = DEFAULT_PATH)
+ json = JSON.load_file(path)
+ elements = json.map do |json_element|
+ ProfileElement.from_json(json_element)
+ end
+ new(elements)
+ end
+ end
+
+ # Constructor
+ #
+ # @param elements [Array] List of profile elements
+ def initialize(elements)
+ @elements = elements
+ @index = create_index(elements)
+ end
+
+ # Find an element by its key
+ #
+ # @param key [String] Element key (e.g., "networking/base")
+ def find_element(key)
+ element = @index.dig(*key.split(SEPARATOR))
+ element if element.is_a?(ProfileElement)
+ rescue TypeError
+ nil
+ end
+
+ private
+
+ # Creates an index to make searching for an element faster and easier.
+ def create_index(elements)
+ elements.each_with_object({}) do |e, index|
+ index[e.short_key] = if e.children.empty?
+ e
+ else
+ create_index(e.children)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/service/lib/agama/autoyast/profile_fetcher.rb b/service/lib/agama/autoyast/profile_fetcher.rb
new file mode 100644
index 0000000000..2673ea19a7
--- /dev/null
+++ b/service/lib/agama/autoyast/profile_fetcher.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2025] 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"
+
+# :nodoc:
+module Agama
+ module AutoYaST
+ # Retrieves an AutoYaST profile into an Agama one.
+ #
+ # It supports AutoYaST dynamic profiles features: pre-scripts, rules/classes and ERB.
+ #
+ # It generates an AutoYaST profile that can be converted into an Agama configuration using the
+ # {Agama::AutoYaST::Converter} class.
+ class ProfileFetcher
+ # @param profile_url [String] Profile URL
+ def initialize(profile_url)
+ @profile_url = profile_url
+ end
+
+ # Converts the profile into a set of files that Agama can process.
+ # @return [Hash,nil] an evaluated AutoYaST profile
+ def fetch
+ import_yast
+ read_profile
+ end
+
+ private
+
+ attr_reader :profile_url
+
+ # @return [Hash, nil] AutoYaST profile
+ def read_profile
+ FileUtils.mkdir_p(Yast::AutoinstConfig.profile_dir)
+
+ # fetch the profile
+ Yast::AutoinstConfig.ParseCmdLine(profile_url)
+ return unless Yast::ProfileLocation.Process
+
+ # put the profile in the tmp directory
+ FileUtils.cp(
+ Yast::AutoinstConfig.xml_tmpfile,
+ tmp_profile_path
+ )
+
+ loop do
+ Yast::Profile.ReadXML(tmp_profile_path)
+ run_pre_scripts
+ break unless File.exist?(Yast::AutoinstConfig.modified_profile)
+
+ FileUtils.cp(Yast::AutoinstConfig.modified_profile, tmp_profile_path)
+ FileUtils.rm(Yast::AutoinstConfig.modified_profile)
+ end
+
+ Yast::ProfileHash.new(Yast::Profile.current)
+ end
+
+ def run_pre_scripts
+ pre_scripts = Yast::Profile.current.fetch_as_hash("scripts")
+ .fetch_as_array("pre-scripts")
+ .map { |h| Y2Autoinstallation::PreScript.new(h) }
+ script_runner = Y2Autoinstall::ScriptRunner.new
+
+ pre_scripts.each do |script|
+ script.create_script_file
+ script_runner.run(script)
+ end
+ end
+
+ def tmp_profile_path
+ @tmp_profile_path ||= File.join(
+ Yast::AutoinstConfig.profile_dir,
+ "autoinst.xml"
+ )
+ end
+
+ def import_yast
+ Yast.import "AutoinstConfig"
+ Yast.import "AutoinstScripts"
+ Yast.import "Profile"
+ Yast.import "ProfileLocation"
+ end
+ end
+ end
+end
diff --git a/service/lib/agama/autoyast/profile_reporter.rb b/service/lib/agama/autoyast/profile_reporter.rb
new file mode 100644
index 0000000000..37511d4619
--- /dev/null
+++ b/service/lib/agama/autoyast/profile_reporter.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2025] 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"
+
+# :nodoc:
+module Agama
+ module AutoYaST
+ # Reports the problems found by the {ProfileChecker} using the questions client.
+ class ProfileReporter
+ include Yast::I18n
+
+ # Constructor
+ #
+ # @param questions_client [Agama::DBus::Clients::Questions]
+ # @param logger [Logger]
+ def initialize(questions_client, logger)
+ textdomain "agama"
+
+ @questions_client = questions_client
+ @logger = logger
+ end
+
+ # Reports the problems and decide whether to continue or not.
+ #
+ # @param elements [Array] List of unsupported elements.
+ def report(elements)
+ keys = elements.map(&:key).join(", ")
+ unsupported = elements.select { |e| e.support == :no }
+ planned = elements.select { |e| e.support == :planned }
+
+ message = format(
+ _("Found unsupported elements in the AutoYaST profile: %{keys}."), keys: keys
+ )
+ question = Agama::Question.new(
+ qclass: "autoyast.unsupported",
+ text: message,
+ options: [:Continue, :Abort],
+ default_option: :Continue,
+ data: {
+ "planned" => planned.map(&:key).join(","),
+ "unsupported" => unsupported.map(&:key).join(",")
+ }
+ )
+
+ questions_client.ask(question) do |question_client|
+ question_client.answer == :Continue
+ end
+ end
+
+ private
+
+ attr_reader :questions_client, :logger
+ end
+ end
+end
diff --git a/service/lib/agama/commands.rb b/service/lib/agama/commands.rb
new file mode 100644
index 0000000000..14c42ddd24
--- /dev/null
+++ b/service/lib/agama/commands.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+#
+# Copyright (c) [2025] 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.
+
+module Agama
+ # Module which contains the Agama commands.
+ #
+ # TODO: move all the commands to this namespace.
+ module Commands
+ end
+end
+
+require "agama/autoyast"
diff --git a/service/lib/agama/commands/agama_autoyast.rb b/service/lib/agama/commands/agama_autoyast.rb
new file mode 100644
index 0000000000..11e9685d21
--- /dev/null
+++ b/service/lib/agama/commands/agama_autoyast.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+#
+# Copyright (c) [2025] 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 "agama/autoyast/converter"
+require "agama/autoyast/profile_fetcher"
+require "agama/autoyast/profile_reporter"
+require "agama/autoyast/profile_checker"
+require "agama/cmdline_args"
+require "agama/dbus/clients/questions"
+
+module Agama
+ # :nodoc:
+ module Commands
+ class CouldNotFetchProfile < StandardError; end
+ class CouldNotWriteAgamaConfig < StandardError; end
+
+ # Command to convert an AutoYaST profile to an Agama configuration.
+ #
+ # It fetches the profile, checks for unsupported elements and converts it to an Agama
+ # configuration file.
+ #
+ # @param url [String] URL of the AutoYaST profile
+ # @param dir [String] Directory to write the converted profile
+ class AgamaAutoYaST
+ def initialize(url, directory)
+ @url = url
+ @directory = directory
+ @logger = Logger.new($stdout)
+ end
+
+ # Run the command fetching, checking, converting and writing the Agama configuration.
+ def run
+ profile = fetch_profile
+ unsupported = check_profile(profile)
+ return false unless report_unsupported(unsupported)
+
+ write_agama_config(profile)
+ end
+
+ private
+
+ attr_reader :url, :directory, :logger
+
+ # Fetch the AutoYaST profile from the given URL.
+ def fetch_profile
+ Agama::AutoYaST::ProfileFetcher.new(url).fetch
+ rescue RuntimeError
+ raise CouldNotFetchProfile
+ end
+
+ # Check the profile for unsupported
+ def check_profile(profile)
+ checker = Agama::AutoYaST::ProfileChecker.new
+ elements = checker.find_unsupported(profile)
+ logger.info "Found unsupported AutoYaST elements: #{elements.map(&:key)}"
+ elements
+ end
+
+ def report_unsupported(elements)
+ return true if elements.empty? || !report?
+
+ reporter = Agama::AutoYaST::ProfileReporter.new(questions_client, logger)
+ reporter.report(elements)
+ end
+
+ def write_agama_config(profile)
+ converter = Agama::AutoYaST::Converter.new
+ agama_config = converter.to_agama(profile)
+ FileUtils.mkdir_p(directory)
+ File.write(File.join(directory, "autoinst.json"), agama_config.to_json)
+ rescue StandardError
+ raise CouldNotWriteAgamaConfig
+ end
+
+ def questions_client
+ @questions_client ||= Agama::DBus::Clients::Questions.new(logger: logger)
+ end
+
+ # Whether the report is enabled or not.
+ def report?
+ cmdline = CmdlineArgs.read_from("/proc/cmdline")
+ cmdline.data.fetch("ay_check", "1") != "0"
+ end
+ end
+ end
+end
diff --git a/service/lib/agama/dbus/y2dir/software/modules/PackageCallbacks.rb b/service/lib/agama/dbus/y2dir/software/modules/PackageCallbacks.rb
index 506c2145ad..6887b88388 100644
--- a/service/lib/agama/dbus/y2dir/software/modules/PackageCallbacks.rb
+++ b/service/lib/agama/dbus/y2dir/software/modules/PackageCallbacks.rb
@@ -41,6 +41,10 @@ def InitPackageCallbacks(logger = nil)
Agama::Software::Callbacks::Media.new(
questions_client, logger
).setup
+
+ Agama::Software::Callbacks::Provide.new(
+ questions_client, logger
+ ).setup
end
# Returns the client to ask questions
@@ -59,4 +63,3 @@ def questions_client
PackageCallbacks = PackageCallbacksClass.new
PackageCallbacks.main
end
-
diff --git a/service/lib/agama/software/callbacks.rb b/service/lib/agama/software/callbacks.rb
index a6e65f3e86..9a46fc60c5 100644
--- a/service/lib/agama/software/callbacks.rb
+++ b/service/lib/agama/software/callbacks.rb
@@ -29,4 +29,6 @@ module Callbacks
require "agama/software/callbacks/media"
require "agama/software/callbacks/progress"
+require "agama/software/callbacks/provide"
+require "agama/software/callbacks/script"
require "agama/software/callbacks/signature"
diff --git a/service/lib/agama/software/callbacks/media.rb b/service/lib/agama/software/callbacks/media.rb
index ae76a6df6e..6eb1ac8d1c 100644
--- a/service/lib/agama/software/callbacks/media.rb
+++ b/service/lib/agama/software/callbacks/media.rb
@@ -54,8 +54,25 @@ def setup
# @return [String]
# @see https://github.com/yast/yast-yast2/blob/19180445ab935a25edd4ae0243aa7a3bcd09c9de/library/packages/src/modules/PackageCallbacks.rb#L620
# rubocop:disable Metrics/ParameterLists
- def media_change(_error_code, error, url, _product, _current, _current_label, _wanted,
- _wanted_label, _double_sided, _devices, _current_device)
+ def media_change(error_code, error, url, product, current, current_label, wanted,
+ wanted_label, double_sided, devices, current_device)
+ logger.debug(
+ format("MediaChange callback: error_code: %s, error: %s, url: %s, product: %s, " \
+ "current: %s, current_label: %s, wanted: %s, wanted_label: %s, " \
+ "double_sided: %s, devices: %s, current_device",
+ error_code,
+ error,
+ Yast::URL.HidePassword(url),
+ product,
+ current,
+ current_label,
+ wanted,
+ wanted_label,
+ double_sided,
+ devices,
+ current_device)
+ )
+
question = Agama::Question.new(
qclass: "software.medium_error",
text: error,
diff --git a/service/lib/agama/software/callbacks/progress.rb b/service/lib/agama/software/callbacks/progress.rb
index 60ebb3f77f..84f13ad846 100644
--- a/service/lib/agama/software/callbacks/progress.rb
+++ b/service/lib/agama/software/callbacks/progress.rb
@@ -19,7 +19,10 @@
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.
+require "logger"
require "yast"
+require "agama/question"
+require "agama/dbus/clients/questions"
Yast.import "Pkg"
@@ -28,21 +31,34 @@ module Software
module Callbacks
# This class represents the installer status
class Progress
+ include Yast::I18n
+
class << self
- def setup(pkg_count, progress)
- new(pkg_count, progress).setup
+ def setup(pkg_count, progress, logger)
+ new(pkg_count, progress, logger).setup
end
end
- def initialize(pkg_count, progress)
+ def initialize(pkg_count, progress, logger)
+ textdomain "agama"
+
@total = pkg_count
@installed = 0
@progress = progress
+ @logger = logger || ::Logger.new($stdout)
end
def setup
Yast::Pkg.CallbackStartPackage(
- fun_ref(method(:start_package), "void (string, string, string, integer, boolean)")
+ Yast::FunRef.new(
+ method(:start_package), "void (string, string, string, integer, boolean)"
+ )
+ )
+
+ Yast::Pkg.CallbackDonePackage(
+ Yast::FunRef.new(
+ method(:done_package), "string (integer, string)"
+ )
)
end
@@ -51,12 +67,52 @@ def setup
# @return [Agama::Progress]
attr_reader :progress
- def fun_ref(method, signature)
- Yast::FunRef.new(method, signature)
+ # @return [String,nil]
+ attr_accessor :current_package
+
+ # @return [Logger]
+ attr_reader :logger
+
+ # @return [Agama::DBus::Clients::Questions]
+ def questions_client
+ @questions_client ||= Agama::DBus::Clients::Questions.new(logger: logger)
end
def start_package(package, _file, _summary, _size, _other)
progress.step("Installing #{package}")
+ self.current_package = package
+ end
+
+ def done_package(error_code, description)
+ return "" if error_code == 0
+
+ logger.error("Package #{current_package} failed: #{description}")
+
+ # TRANSLATORS: %s is a package name
+ text = _("Package %s could not be installed.") % current_package
+
+ question = Agama::Question.new(
+ qclass: "software.install_error",
+ text: text,
+ options: [:Retry, :Cancel, :Ignore],
+ default_option: :Retry,
+ data: { "description" => description }
+ )
+
+ questions_client.ask(question) do |question_client|
+ case question_client.answer
+ when :Retry
+ "R"
+ when :Cancel
+ "C"
+ when :Ignore
+ "I"
+ else
+ logger.error("Unexpected response #{question_client.answer.inspect}, " \
+ "ignoring the package error")
+ "I"
+ end
+ end
end
def msg
diff --git a/service/lib/agama/software/callbacks/provide.rb b/service/lib/agama/software/callbacks/provide.rb
new file mode 100644
index 0000000000..eccbd635d4
--- /dev/null
+++ b/service/lib/agama/software/callbacks/provide.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2025] 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 "logger"
+require "yast"
+require "agama/question"
+
+Yast.import "Pkg"
+
+module Agama
+ module Software
+ module Callbacks
+ # Provide callbacks
+ class Provide
+ include Yast::I18n
+
+ # From https://github.com/openSUSE/libzypp/blob/d90a93fc2a248e6592bd98114f82a0b88abadb72/zypp/ZYppCallbacks.h#L111
+ NO_ERROR = 0
+ NOT_FOUND = 1
+ IO_ERROR = 2
+ INVALID = 3
+
+ # Constructor
+ #
+ # @param questions_client [Agama::DBus::Clients::Questions]
+ # @param logger [Logger]
+ def initialize(questions_client, logger)
+ textdomain "agama"
+ @questions_client = questions_client
+ @logger = logger || ::Logger.new($stdout)
+ end
+
+ # Register the callbacks
+ def setup
+ Yast::Pkg.CallbackDoneProvide(
+ Yast::FunRef.new(method(:done_provide), "string (integer, string, string)")
+ )
+ end
+
+ # DoneProvide callback
+ #
+ # @return [String] "I" for ignore, "R" for retry and "C" for abort (not implemented)
+ # @see https://github.com/yast/yast-yast2/blob/19180445ab935a25edd4ae0243aa7a3bcd09c9de/library/packages/src/modules/PackageCallbacks.rb#L620
+ def done_provide(error, reason, name)
+ args = [error, reason, name]
+ logger.debug "DoneProvide callback: #{args.inspect}"
+
+ message = case error
+ when NO_ERROR, NOT_FOUND
+ # "Not found" (error 1) is handled by the MediaChange callback.
+ nil
+ when IO_ERROR
+ Yast::Builtins.sformat(_("Package %1 could not be downloaded (input/output error)."),
+ name)
+ when INVALID
+ Yast::Builtins.sformat(_("Package %1 is broken, integrity check has failed."), name)
+ else
+ logger.warn "DoneProvide: unknown error: '#{error}'"
+ end
+
+ return if message.nil?
+
+ question = Agama::Question.new(
+ qclass: "software.provide_error",
+ text: message,
+ options: [:Retry, :Ignore],
+ default_option: :Retry,
+ data: { "reason" => reason }
+ )
+ questions_client.ask(question) do |question_client|
+ (question_client.answer == :Retry) ? "R" : "I"
+ end
+ end
+
+ private
+
+ # @return [Agama::DBus::Clients::Questions]
+ attr_reader :questions_client
+
+ # @return [Logger]
+ attr_reader :logger
+ end
+ end
+ end
+end
diff --git a/service/lib/agama/software/callbacks/script.rb b/service/lib/agama/software/callbacks/script.rb
new file mode 100644
index 0000000000..d8cbc846b4
--- /dev/null
+++ b/service/lib/agama/software/callbacks/script.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2025] 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 "agama/question"
+
+Yast.import "Pkg"
+
+module Agama
+ module Software
+ module Callbacks
+ # Script callbacks
+ class Script
+ include Yast::I18n
+
+ # Constructor
+ #
+ # @param questions_client [Agama::DBus::Clients::Questions]
+ # @param logger [Logger]
+ def initialize(questions_client, logger)
+ textdomain "agama"
+ @questions_client = questions_client
+ @logger = logger
+ end
+
+ # Register the callbacks
+ def setup
+ Yast::Pkg.CallbackScriptProblem(
+ Yast::FunRef.new(method(:ScriptProblem), "string (string)")
+ )
+ end
+
+ # DoneProvide callback
+ #
+ # @param description [String] Problem description
+ # @return [String] "I" for ignore, "R" for retry and "C" for abort (not implemented)
+ # @see https://github.com/yast/yast-yast2/blob/19180445ab935a25edd4ae0243aa7a3bcd09c9de/library/packages/src/modules/PackageCallbacks.rb#L620
+ def script_problem(description)
+ logger.debug "ScriptProblem callback: description: #{description}"
+
+ message = _("There was a problem running a package script.")
+ question = Agama::Question.new(
+ qclass: "software.script_problem",
+ text: message,
+ options: [:Retry, :Ignore],
+ default_option: :Retry,
+ data: { "details" => description }
+ )
+ questions_client.ask(question) do |question_client|
+ (question_client.answer == :Retry) ? "R" : "I"
+ end
+ end
+
+ private
+
+ # @return [Agama::DBus::Clients::Questions]
+ attr_reader :questions_client
+
+ # @return [Logger]
+ attr_reader :logger
+ end
+ end
+ end
+end
diff --git a/service/lib/agama/software/manager.rb b/service/lib/agama/software/manager.rb
index ba3881b7fc..73d1d04e73 100644
--- a/service/lib/agama/software/manager.rb
+++ b/service/lib/agama/software/manager.rb
@@ -183,7 +183,7 @@ def install
steps = proposal.packages_count
start_progress_with_size(steps)
- Callbacks::Progress.setup(steps, progress)
+ Callbacks::Progress.setup(steps, progress, logger)
# TODO: error handling
commit_result = Yast::Pkg.Commit({})
@@ -201,10 +201,16 @@ def install
# Writes the repositories information to the installed system
def finish
+ # remove the dir:///run/initramfs/live/install repository and similar
+ remove_local_repos
Yast::Pkg.SourceSaveAll
Yast::Pkg.TargetFinish
# copy the libzypp caches to the target
- copy_zypp_to_target
+ if Agama::Software::Repository.all.empty?
+ logger.info("No repository defined, not copying the libzypp caches")
+ else
+ copy_zypp_to_target
+ end
registration.finish
end
@@ -700,6 +706,11 @@ def update_repositories(new_product)
Yast::Pkg.SourceSaveAll
end
end
+
+ # remove all local repositories
+ def remove_local_repos
+ Agama::Software::Repository.all.select(&:local?).each(&:delete!)
+ end
end
end
end
diff --git a/service/lib/agama/software/repositories_manager.rb b/service/lib/agama/software/repositories_manager.rb
index 63a4d08eef..6761d99ba5 100644
--- a/service/lib/agama/software/repositories_manager.rb
+++ b/service/lib/agama/software/repositories_manager.rb
@@ -71,7 +71,11 @@ def load
repositories.each do |repo|
if repo.probe
repo.enable!
- repo.refresh
+ # In some rare scenarios although the repository probe succeeded the refresh might fail
+ # with network timeout. In that case disable the repository to avoid implicitly
+ # refreshing it again in the Pkg.SourceLoad call which could time out again, effectively
+ # doubling the total timeout.
+ repo.disable! unless repo.refresh
else
repo.disable!
end
diff --git a/service/lib/tasks/autoyast.rb b/service/lib/tasks/autoyast.rb
new file mode 100644
index 0000000000..e4cea89393
--- /dev/null
+++ b/service/lib/tasks/autoyast.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2024] 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 "agama/autoyast/profile_description"
+
+module Agama
+ module Tasks
+ # Generates the AutoYaST compatibility reference
+ #
+ # The document describes which elements are supported, unsupported or planned to be supported.
+ # It uses markdown to make it easier to integrate the document in Agama's website.
+ class AutoYaSTCompatGenerator
+ attr_reader :description
+
+ def initialize
+ @description = Agama::AutoYaST::ProfileDescription.load
+ end
+
+ # Generates the document in Markdown format
+ def generate
+ lines = ["# AutoYaST compatibility reference"]
+
+ top_level = description.elements.select(&:top_level?)
+
+ top_level.each do |e|
+ lines.concat(section(e))
+ end
+
+ lines.join("\n")
+ end
+
+ private
+
+ def section(element, level = 1)
+ title = "#" * (level + 1)
+ lines = ["#{title} #{element.key}"]
+
+ lines << ""
+ lines << notes_for(element)
+ lines << ""
+
+ scalar, complex = element.children.partition do |e|
+ e.children.empty?
+ end
+
+ lines.concat(elements_table(scalar))
+
+ complex.each_with_object(lines) do |e, all|
+ all.concat(section(e, level + 1))
+ end
+ end
+
+ # Generates a table describing the support level of the elements.
+ #
+ # @param elements [Array] Elements to describe.
+ def elements_table(elements)
+ return [] if elements.empty?
+
+ lines = [
+ "| AutoYaST | Supported | Agama | Notes |",
+ "|----------|-----------|-------|-------|"
+ ]
+ elements.each do |e|
+ agama_key = e.agama ? "`#{e.agama}`" : ""
+ lines << "| `#{e.short_key}` | #{e.support} | #{agama_key} | #{e.notes} |"
+ end
+ lines << ""
+ lines
+ end
+
+ # Generates the notes for a given element.
+ #
+ # @param element [ProfileElement] Profile element to generate the notes for
+ def notes_for(element)
+ content = case element.support
+ when :yes
+ "This section is supported."
+ when :no
+ "This section is not supported."
+ when :planned
+ "There are plans to support this section in the future."
+ when :partial
+ "There is partial support for this section."
+ else
+ "Support for this element is still undecided."
+ end
+
+ element.notes ? "#{content} #{element.notes}" : content
+ end
+ end
+ end
+end
diff --git a/service/package/rubygem-agama-yast.changes b/service/package/rubygem-agama-yast.changes
index e3c9b47e37..66a07b60b4 100644
--- a/service/package/rubygem-agama-yast.changes
+++ b/service/package/rubygem-agama-yast.changes
@@ -1,3 +1,34 @@
+-------------------------------------------------------------------
+Wed Feb 12 08:16:38 UTC 2025 - Imobach Gonzalez Sosa
+
+- Report unsupported AutoYaST elements
+ (gh#agama-project/agama#1976).
+
+-------------------------------------------------------------------
+Tue Feb 11 15:11:18 UTC 2025 - Ladislav Slezák
+
+- Fixed possible double timeout when refreshing the installation
+ repository (bsc#1236820)
+
+-------------------------------------------------------------------
+Fri Feb 7 16:29:28 UTC 2025 - Imobach Gonzalez Sosa
+
+- Report problems when trying to download a package, install it
+ or run a script (gh#agama/agama-project#1932).
+
+-------------------------------------------------------------------
+Fri Feb 7 16:16:14 UTC 2025 - José Iván López González
+
+- Adapt tests to use the BLS size requirements
+ (gh#agama-project/agama#1979).
+
+-------------------------------------------------------------------
+Wed Feb 5 14:32:29 UTC 2025 - Ladislav Slezák
+
+- Disable the local media in the installed system, esp. the
+ offline repository with URL dir:///run/initramfs/live/install
+ (bsc#1236813)
+
-------------------------------------------------------------------
Wed Jan 29 16:28:32 UTC 2025 - Josef Reidinger
diff --git a/service/po/ca.po b/service/po/ca.po
index 9e1c2fc47c..a693ef2b6e 100644
--- a/service/po/ca.po
+++ b/service/po/ca.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-10-30 13:48+0000\n"
"Last-Translator: David Medina \n"
"Language-Team: Catalan ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "No s'ha pogut llegir el repositori %s."
@@ -113,14 +139,14 @@ msgstr "No s'ha pogut llegir el repositori %s."
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Encara no s'ha seleccionat el producte."
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "El producte ha d'estar registrat."
diff --git a/service/po/cs.po b/service/po/cs.po
index f6179d10f7..0f2692bb12 100644
--- a/service/po/cs.po
+++ b/service/po/cs.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-11-13 18:48+0000\n"
"Last-Translator: Jan Papež \n"
"Language-Team: Czech ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "Nelze načíst repozitář \"%s\""
@@ -113,14 +139,14 @@ msgstr "Nelze načíst repozitář \"%s\""
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Není vybrán žádný produkt"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "Produkt musí být zaregistrován"
diff --git a/service/po/de.po b/service/po/de.po
index fb492f1a7f..d955ec1daa 100644
--- a/service/po/de.po
+++ b/service/po/de.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-12-12 06:48+0000\n"
"Last-Translator: Ettore Atalan \n"
"Language-Team: German ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "Repositorium „%s“ konnte nicht gelesen werden"
@@ -113,14 +139,14 @@ msgstr "Repositorium „%s“ konnte nicht gelesen werden"
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Produkt noch nicht ausgewählt"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "Produkt muss registriert sein"
diff --git a/service/po/es.po b/service/po/es.po
index e42213d122..5803549beb 100644
--- a/service/po/es.po
+++ b/service/po/es.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-12-11 16:48+0000\n"
"Last-Translator: \"Marina J. Donis\" \n"
"Language-Team: Spanish ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "No se pudo leer el repositorio \"%s\""
@@ -113,14 +139,14 @@ msgstr "No se pudo leer el repositorio \"%s\""
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Producto aún no seleccionado"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "El producto debe estar registrado"
diff --git a/service/po/fi.po b/service/po/fi.po
index 70f54b772a..b5f81d2a86 100644
--- a/service/po/fi.po
+++ b/service/po/fi.po
@@ -7,45 +7,74 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
-"PO-Revision-Date: 2025-01-16 02:34+0000\n"
-"Last-Translator: Automatically generated\n"
-"Language-Team: none\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
+"PO-Revision-Date: 2025-01-22 13:50+0000\n"
+"Last-Translator: Timo Jyrinki \n"
+"Language-Team: Finnish \n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 5.9.2\n"
#. Runs the config phase
#: service/lib/agama/manager.rb:93
msgid "Analyze disks"
-msgstr ""
+msgstr "Levyjen analysointi"
#: service/lib/agama/manager.rb:93
msgid "Configure software"
-msgstr ""
+msgstr "Ohjelmistojen asetukset"
#. Runs the install phase
#. rubocop:disable Metrics/AbcSize
#: service/lib/agama/manager.rb:116
msgid "Prepare disks"
-msgstr ""
+msgstr "Levyjen valmistelu"
#: service/lib/agama/manager.rb:117
msgid "Install software"
-msgstr ""
+msgstr "Ohjelmistojen asennus"
#: service/lib/agama/manager.rb:118
msgid "Configure the system"
-msgstr ""
+msgstr "Järjestelmän asetukset"
#. rubocop:enable Metrics/AbcSize
-#: service/lib/agama/manager.rb:156
+#: service/lib/agama/manager.rb:157
msgid "Load software translations"
-msgstr ""
+msgstr "Lataa ohjelmistojen käännökset"
-#: service/lib/agama/manager.rb:157
+#: service/lib/agama/manager.rb:158
msgid "Load storage translations"
+msgstr "Lataa tallennustilan käännökset"
+
+#. TRANSLATORS: %s is a package name
+#: service/lib/agama/software/callbacks/progress.rb:92
+#, c-format
+msgid "Package %s could not be installed."
+msgstr ""
+
+#. "Not found" (error 1) is handled by the MediaChange callback.
+#: service/lib/agama/software/callbacks/provide.rb:71
+#, ycp-format
+msgid "Package %1 could not be downloaded (input/output error)."
+msgstr ""
+
+#: service/lib/agama/software/callbacks/provide.rb:74
+#, ycp-format
+msgid "Package %1 is broken, integrity check has failed."
+msgstr ""
+
+#. DoneProvide callback
+#.
+#. @param description [String] Problem description
+#. @return [String] "I" for ignore, "R" for retry and "C" for abort (not implemented)
+#. @see https://github.com/yast/yast-yast2/blob/19180445ab935a25edd4ae0243aa7a3bcd09c9de/library/packages/src/modules/PackageCallbacks.rb#L620
+#: service/lib/agama/software/callbacks/script.rb:59
+msgid "There was a problem running a package script."
msgstr ""
#. Callback to handle unsigned files
@@ -55,12 +84,12 @@ msgstr ""
#: service/lib/agama/software/callbacks/signature.rb:63
#, perl-brace-format
msgid "The file %{filename} from repository %{repo_name} (%{repo_url})"
-msgstr ""
+msgstr "Tiedosto %{filename} ohjelmalähteestä %{repo_name} (%{repo_url})"
#: service/lib/agama/software/callbacks/signature.rb:67
#, perl-brace-format
msgid "The file %{filename}"
-msgstr ""
+msgstr "Tiedosto %{filename}"
#: service/lib/agama/software/callbacks/signature.rb:71
#, perl-brace-format
@@ -68,6 +97,8 @@ msgid ""
"%{source} is not digitally signed. The origin and integrity of the file "
"cannot be verified. Use it anyway?"
msgstr ""
+"%{source} ei ole allekirjoitettu digitaalisesti. Tiedoston lähdettä ja "
+"eheyttä ei voida varmistaa. Käytetäänkö sitä silti?"
#. Callback to handle signature verification failures
#.
@@ -79,43 +110,45 @@ msgid ""
"The key %{id} (%{name}) with fingerprint %{fingerprint} is unknown. Do you "
"want to trust this key?"
msgstr ""
+"Avain %{id} (%{name}) sormenjäljellä %{fingerprint} on tuntematon. "
+"Luotetaanko tähän avaimeen?"
#. Should an error be raised?
#: service/lib/agama/software/manager.rb:141
msgid "Initializing sources"
-msgstr ""
+msgstr "Otetaan ohjelmalähteitä käyttöön"
#: service/lib/agama/software/manager.rb:146
msgid "Refreshing repositories metadata"
-msgstr ""
+msgstr "Päivitetään ohjelmalähteiden metatietoja"
#: service/lib/agama/software/manager.rb:147
msgid "Calculating the software proposal"
-msgstr ""
+msgstr "Lasketaan ohjelmistoehdotusta"
#. Issues related to the software proposal.
#.
#. Repositories that could not be probed are reported as errors.
#.
#. @return [Array]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
-msgstr ""
+msgstr "Ohjelmalähdettä \"%s\" ei voi lukea"
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
-msgstr ""
+msgstr "Tuotetta ei ole vielä valittu"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
-msgstr ""
+msgstr "Tuote tulee olla rekisteröity"
#. Returns solver error messages from the last attempt
#.
@@ -123,7 +156,7 @@ msgstr ""
#: service/lib/agama/software/proposal.rb:225
#, c-format
msgid "Found %s dependency issues."
-msgstr ""
+msgstr "Löydettiin %s riippuvuusongelmaa."
#. Issue for not found device.
#.
@@ -131,17 +164,17 @@ msgstr ""
#. @return [Agama::Issue]
#: service/lib/agama/storage/config_checker.rb:87
msgid "No device found for a mandatory drive"
-msgstr ""
+msgstr "Tarvittavalle asemalle ei löydy laitetta"
#: service/lib/agama/storage/config_checker.rb:89
msgid "No device found for a mandatory partition"
-msgstr ""
+msgstr "Tarvittavalle osiolle ei löydy laitetta"
#. TRANSLATORS: %s is the replaced by a mount path (e.g., "/home").
#: service/lib/agama/storage/config_checker.rb:118
#, c-format
msgid "Missing file system type for '%s'"
-msgstr ""
+msgstr "Tiedostojärjestelmän tyyppi puuttuu kohteelle ”%s”"
#. TRANSLATORS: %{filesystem} is replaced by a file system type (e.g., "Btrfs") and
#. %{path} is replaced by a mount path (e.g., "/home").
@@ -149,6 +182,7 @@ msgstr ""
#, perl-brace-format
msgid "The file system type '%{filesystem}' is not suitable for '%{path}'"
msgstr ""
+"Tiedostojärjestelmän tyyppi ”%{filesystem}” ei ole sopiva kohteelle ”%{path}”"
#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
#. (e.g., 'luks1', 'random_swap').
@@ -157,20 +191,21 @@ msgstr ""
msgid ""
"No passphrase provided (required for using the method '%{crypt_method}')."
msgstr ""
+"Salauslausetta ei ole annettu (vaaditaan salaustavalle ”%{crypt_method}”)."
#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
#. (e.g., 'luks1', 'random_swap').
#: service/lib/agama/storage/config_checker.rb:196
#, perl-brace-format
msgid "Encryption method '%{crypt_method}' is not available in this system."
-msgstr ""
+msgstr "Salaustapa ”%{crypt_method}” ei ole saatavilla tälle järjestelmälle."
#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
#. (e.g., 'luks1', 'random_swap').
#: service/lib/agama/storage/config_checker.rb:226
#, perl-brace-format
msgid "'%{crypt_method}' is not a suitable method to encrypt the device."
-msgstr ""
+msgstr "”%{crypt_method}” ei ole tämän laitteen salaukseen sopiva salaustapa."
#. TRANSLATORS: %s is the replaced by a device alias (e.g., "disk1").
#: service/lib/agama/storage/config_checker.rb:276
@@ -178,6 +213,7 @@ msgstr ""
msgid ""
"The device '%s' is used several times as target device for physical volumes"
msgstr ""
+"Laitetta ”%s” on käytetty useaan kertaan fyysisten taltioiden kohdelaitteena"
#. TRANSLATORS: %s is the replaced by a device alias (e.g., "pv1").
#: service/lib/agama/storage/config_checker.rb:350
@@ -204,6 +240,7 @@ msgstr ""
msgid ""
"'%{crypt_method}' is not a suitable method to encrypt the physical volumes."
msgstr ""
+"Salaustapa ”%{crypt_method}” ei ole sopiva fyysisten taltioiden salaamiseen."
#. Text of the reason preventing to shrink because there is no content.
#.
@@ -214,48 +251,51 @@ msgid ""
"case the device does contain a file system or a storage system that is not "
"supported, resizing will most likely cause data loss."
msgstr ""
+"Tiedostojärjestelmää tai tallennusjärjestelmää ei havaittu laitteella. "
+"Mikäli laitteella on kuitenkin tiedosto- tai tallennusjärjestelmä jota ei "
+"tueta, laitteen koon muuttaminen aiheuttaa luultavasti tietojen häviämistä."
#. Text of the reason preventing to shrink because there is no valid minimum size.
#.
#. @return [String, nil] nil if there is a minimum size or there is any other reasons.
#: service/lib/agama/storage/device_shrinking.rb:162
msgid "Shrinking is not supported by this device"
-msgstr ""
+msgstr "Tämä laite ei tue koon pienentämistä"
#. Probes storage devices and performs an initial proposal
#: service/lib/agama/storage/manager.rb:120
msgid "Activating storage devices"
-msgstr ""
+msgstr "Otetaan tallennuslaitteita käyttöön"
#: service/lib/agama/storage/manager.rb:121
msgid "Probing storage devices"
-msgstr ""
+msgstr "Havaitaan tallennuslaitteita"
#: service/lib/agama/storage/manager.rb:122
msgid "Calculating the storage proposal"
-msgstr ""
+msgstr "Lasketaan tallennustilan käyttöehdotusta"
#: service/lib/agama/storage/manager.rb:123
msgid "Selecting Linux Security Modules"
-msgstr ""
+msgstr "Valitaan Linuxin tietoturvamoduuleita (LSM)"
#. Prepares the partitioning to install the system
#: service/lib/agama/storage/manager.rb:131
msgid "Preparing bootloader proposal"
-msgstr ""
+msgstr "Valmistellaan alkulatausohjelman ehdotusta"
#. then also apply changes to that proposal
#: service/lib/agama/storage/manager.rb:138
msgid "Adding storage-related packages"
-msgstr ""
+msgstr "Lisätään tallennukseen liittyviä paketteja"
#: service/lib/agama/storage/manager.rb:139
msgid "Preparing the storage devices"
-msgstr ""
+msgstr "Valmistellaan tallennuslaitteita"
#: service/lib/agama/storage/manager.rb:140
msgid "Writing bootloader sysconfig"
-msgstr ""
+msgstr "Kirjoitetaan alkulatausohjelman asetuksia"
#. Issue representing the proposal is not valid.
#.
@@ -263,20 +303,21 @@ msgstr ""
#: service/lib/agama/storage/proposal.rb:287
msgid "Cannot accommodate the required file systems for installation"
msgstr ""
+"Asennukseen tarvittavia tiedostojärjestelmiä ei saada sovitettua käyttöön"
#. Issue to communicate a generic Y2Storage error.
#.
#. @return [Issue]
#: service/lib/agama/storage/proposal.rb:298
msgid "A problem ocurred while calculating the storage setup"
-msgstr ""
+msgstr "Tallennustila-asetusten laskemisessa tapahtui ongelma"
#. Returns an issue if there is no target device.
#.
#. @return [Issue, nil]
#: service/lib/agama/storage/proposal_strategies/guided.rb:127
msgid "No device selected for installation"
-msgstr ""
+msgstr "Asennukselle ei ole valittu laitetta"
#. Returns an issue if any of the devices required for the proposal is not found
#.
@@ -286,11 +327,13 @@ msgstr ""
msgid "The following selected device is not found in the system: %{devices}"
msgid_plural ""
"The following selected devices are not found in the system: %{devices}"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Seuraava valittu laite ei löydy järjestelmästä: %{devices}"
+msgstr[1] "Seuraavat valitut laitteet eivät löydy järjestelmästä: %{devices}"
#. Recalculates the list of issues
#: service/lib/agama/users.rb:154
msgid ""
"Defining a user, setting the root password or a SSH public key is required"
msgstr ""
+"Joko käyttäjän, pääkäyttäjän salasanan tai julkisen SSH-avaimen "
+"määrittäminen vaaditaan"
diff --git a/service/po/fr.po b/service/po/fr.po
index 82db92caaa..5e67fb3546 100644
--- a/service/po/fr.po
+++ b/service/po/fr.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-04-19 23:43+0000\n"
"Last-Translator: faila fail \n"
"Language-Team: French ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "Impossible de lire le dépôt \"%s\""
@@ -115,14 +141,14 @@ msgstr "Impossible de lire le dépôt \"%s\""
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Le produit n'est pas encore sélectionné"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "Le produit doit être enregistré"
diff --git a/service/po/id.po b/service/po/id.po
index 6bdce0e754..5f5522b6a2 100644
--- a/service/po/id.po
+++ b/service/po/id.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-12-25 18:50+0000\n"
"Last-Translator: Arif Budiman \n"
"Language-Team: Indonesian ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "Tidak dapat membaca repositori \"%s\""
@@ -113,14 +139,14 @@ msgstr "Tidak dapat membaca repositori \"%s\""
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Produk belum dipilih"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "Produk harus didaftarkan"
diff --git a/service/po/ja.po b/service/po/ja.po
index cc89c1ef25..535b4e2aa6 100644
--- a/service/po/ja.po
+++ b/service/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-10-30 00:48+0000\n"
"Last-Translator: Yasuhiko Kamata \n"
"Language-Team: Japanese ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "リポジトリ\"%s\" を読み込むことができませんでした"
@@ -113,14 +139,14 @@ msgstr "リポジトリ\"%s\" を読み込むことができませんでした"
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "まだ製品を選択していません"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "製品を登録しなければなりません"
diff --git a/service/po/ka.po b/service/po/ka.po
index bcf96ca258..14321debd1 100644
--- a/service/po/ka.po
+++ b/service/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-10-31 13:48+0000\n"
"Last-Translator: Temuri Doghonadze \n"
"Language-Team: Georgian ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr ""
@@ -109,14 +135,14 @@ msgstr ""
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr ""
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr ""
diff --git a/service/po/nb_NO.po b/service/po/nb_NO.po
index b8a4e5848b..47273acd61 100644
--- a/service/po/nb_NO.po
+++ b/service/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-06-29 13:46+0000\n"
"Last-Translator: Martin Hansen \n"
"Language-Team: Norwegian Bokmål ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "Kunne ikke lese pakkebrønn \"%s\""
@@ -113,14 +139,14 @@ msgstr "Kunne ikke lese pakkebrønn \"%s\""
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Produkt har ikke blitt valgt ennå"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "Produkt må bli registrert"
diff --git a/service/po/nl.po b/service/po/nl.po
index 6d0e188772..69cc3955c2 100644
--- a/service/po/nl.po
+++ b/service/po/nl.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2025-01-02 16:50+0000\n"
"Last-Translator: Natasha Ament \n"
"Language-Team: Dutch ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "Kan opslagruimte \"%s\" niet lezen"
@@ -113,14 +139,14 @@ msgstr "Kan opslagruimte \"%s\" niet lezen"
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Nog geen product geselecteerd"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "Product moet geregistreerd zijn"
diff --git a/service/po/pt_BR.po b/service/po/pt_BR.po
index 6b2f89be40..bf0c0acb2e 100644
--- a/service/po/pt_BR.po
+++ b/service/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-11-14 15:48+0000\n"
"Last-Translator: Rodrigo Macedo \n"
"Language-Team: Portuguese (Brazil) ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "Não foi possível ler o repositório \"%s\""
@@ -113,14 +139,14 @@ msgstr "Não foi possível ler o repositório \"%s\""
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Produto ainda não selecionado"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "O produto deve ser registrado"
diff --git a/service/po/ru.po b/service/po/ru.po
index b4d4f0aebd..3300ea1517 100644
--- a/service/po/ru.po
+++ b/service/po/ru.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-06-26 10:46+0000\n"
"Last-Translator: Aleksey Fedorov \n"
"Language-Team: Russian ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "Не удалось прочитать репозиторий \"%s\""
@@ -114,14 +140,14 @@ msgstr "Не удалось прочитать репозиторий \"%s\""
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Продукт еще не выбран"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "Продукт должен быть зарегистрирован"
diff --git a/service/po/sv.po b/service/po/sv.po
index 66db01cab8..d4a732b8ad 100644
--- a/service/po/sv.po
+++ b/service/po/sv.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-10-29 12:48+0000\n"
"Last-Translator: Luna Jernberg \n"
"Language-Team: Swedish ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "Kunde inte läsa förråd \"%s\""
@@ -113,14 +139,14 @@ msgstr "Kunde inte läsa förråd \"%s\""
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Produkt har inte valts ännu"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "Produkt måste registreras"
diff --git a/service/po/tr.po b/service/po/tr.po
index 00914bb4bf..1da39df7dc 100644
--- a/service/po/tr.po
+++ b/service/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-11-07 21:48+0000\n"
"Last-Translator: yok4 \n"
"Language-Team: Turkish ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "\"%s\" deposu okunamadı"
@@ -113,14 +139,14 @@ msgstr "\"%s\" deposu okunamadı"
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "Ürün henüz seçilmedi"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "Ürün kayıtlı olmalı"
diff --git a/service/po/zh_Hans.po b/service/po/zh_Hans.po
index fcc7895a0d..29d004ea22 100644
--- a/service/po/zh_Hans.po
+++ b/service/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-19 02:40+0000\n"
+"POT-Creation-Date: 2025-02-09 02:40+0000\n"
"PO-Revision-Date: 2024-07-03 14:46+0000\n"
"Last-Translator: Monstorix \n"
"Language-Team: Chinese (Simplified) ]
-#: service/lib/agama/software/manager.rb:561
+#: service/lib/agama/software/manager.rb:596
#, c-format
msgid "Could not read repository \"%s\""
msgstr "无法读取仓库 “ %s”"
@@ -111,14 +137,14 @@ msgstr "无法读取仓库 “ %s”"
#. Issue when a product is missing
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:571
+#: service/lib/agama/software/manager.rb:606
msgid "Product not selected yet"
msgstr "尚未选择产品"
#. Issue when a product requires registration but it is not registered yet.
#.
#. @return [Agama::Issue]
-#: service/lib/agama/software/manager.rb:580
+#: service/lib/agama/software/manager.rb:615
msgid "Product must be registered"
msgstr "产品必须注册"
diff --git a/service/share/autoyast-compat.json b/service/share/autoyast-compat.json
new file mode 100644
index 0000000000..1cb4cfb8ba
--- /dev/null
+++ b/service/share/autoyast-compat.json
@@ -0,0 +1,440 @@
+[
+ { "key": "add-on", "support": "no" },
+ { "key": "audit-laf", "support": "no" },
+ { "key": "auth-client", "support": "no" },
+ { "key": "configuration_management", "support": "no" },
+ { "key": "deploy_image", "support": "no" },
+ { "key": "dhcp-server", "support": "no" },
+ { "key": "dns-server", "support": "no" },
+ { "key": "fcoe-client", "support": "no" },
+ { "key": "files", "support": "planned" },
+ { "key": "firstboot", "support": "no" },
+ { "key": "ftp-server", "support": "no" },
+ { "key": "general", "support": "no" },
+ { "key": "groups", "support": "no" },
+ { "key": "host", "support": "no" },
+ { "key": "http-server", "support": "no" },
+ {
+ "key": "keyboard",
+ "children": [
+ { "key": "keymap", "support": "yes", "agama": "localization.keyboard" },
+ { "key": "capslock", "support": "no" },
+ { "key": "delay", "support": "no" },
+ { "key": "discaps", "support": "no" },
+ { "key": "numlock", "support": "no" },
+ { "key": "rate", "support": "no" },
+ { "key": "scrlock", "support": "no" },
+ { "key": "tty", "support": "no" }
+ ]
+ },
+ {
+ "key": "language",
+ "children": [
+ { "key": "language", "support": "yes", "agama": "localization.language" },
+ { "key": "languages", "support": "no" }
+ ]
+ },
+ {
+ "key": "networking",
+ "children": [
+ {
+ "key": "backend",
+ "support": "no",
+ "notes": "Only NetworkManager is supported."
+ },
+ { "key": "dhcp_options", "support": "no" },
+ { "key": "dns", "support": "no" },
+ {
+ "key": "ipv6",
+ "support": "yes",
+ "notes": "It affects `method4` and `method6`."
+ },
+ { "key": "keep_install_network", "support": "no" },
+ { "key": "managed", "support": "no" },
+ { "key": "modules", "support": "no" },
+ { "key": "net-udev", "support": "no" },
+ { "key": "routing", "support": "no" },
+ { "key": "s390-devices", "support": "no" },
+ { "key": "setup_before_proposal", "support": "no" },
+ { "key": "strict_IP_check_timeout", "support": "no" },
+ { "key": "virt_bridge_proposal", "support": "no" },
+ {
+ "key": "interfaces[]",
+ "agama": "connections",
+ "notes": "It corresponds to Agama `connections`, but the format is not exactly the same.",
+ "children": [
+ { "key": "device", "agama": "interface", "support": "yes" },
+ { "key": "name", "agama": "id", "support": "yes" },
+ { "key": "description", "support": "no" },
+ {
+ "key": "bootproto",
+ "support": "no",
+ "agama": "method6",
+ "notes": "Different set of values."
+ },
+ {
+ "key": "startmode",
+ "support": "no",
+ "notes": "Do not set up network connections you won't use."
+ },
+ {
+ "key": "lladdr",
+ "support": "yes",
+ "agama": "macAddress"
+ },
+ {
+ "key": "ifplugd_priority",
+ "support": "no",
+ "notes": "Not relevant (no ifplugd support)."
+ },
+ { "key": "usercontrol", "support": "no" },
+ { "key": "dhclient_set_hostname", "support": "no" },
+ {
+ "key": "ipaddr",
+ "support": "yes",
+ "agama": "network.connections[].address[]"
+ },
+ {
+ "key": "prefixlen",
+ "support": "yes",
+ "agama": "network.connections[].address[]"
+ },
+ {
+ "key": "netmask",
+ "support": "yes",
+ "agama": "network.connections[].address[]"
+ },
+ {
+ "key": "aliases",
+ "support": "yes",
+ "agama": "network.connections[].address[]"
+ },
+ {
+ "key": "broadcast",
+ "support": "yes",
+ "agama": "network.connections[].address[]"
+ },
+ {
+ "key": "network",
+ "support": "yes",
+ "agama": "network.connections[].address[]"
+ },
+ {
+ "key": "mtu",
+ "support": "no"
+ },
+ {
+ "key": "ethtool_options",
+ "support": "no"
+ },
+ {
+ "key": "wireless",
+ "support": "yes",
+ "agama": "wireless",
+ "notes": "It uses a different format."
+ },
+ { "key": "dhclient_set_down_link", "support": "no" },
+ { "key": "dhclient_set_default_route", "support": "no" },
+ { "key": "zone", "support": "no" },
+ { "key": "firewall", "support": "no" },
+ { "key": "bonding_master", "support": "planned" },
+ {
+ "key": "bonding_module_opts",
+ "support": "yes",
+ "agama": "network.connections[].bond.options"
+ },
+ {
+ "key": "bonding_slave0",
+ "support": "yes",
+ "agama": "network.connections[].bond.ports"
+ },
+ {
+ "key": "bonding_slave1",
+ "support": "yes",
+ "agama": "network.connections[].bond.ports"
+ },
+ {
+ "key": "bonding_slave2",
+ "support": "yes",
+ "agama": "network.connections[].bond.ports"
+ },
+ {
+ "key": "bonding_slave3",
+ "support": "yes",
+ "agama": "network.connections[].bond.ports"
+ },
+ {
+ "key": "bonding_slave4",
+ "support": "yes",
+ "agama": "network.connections[].bond.ports"
+ },
+ {
+ "key": "bonding_slave5",
+ "support": "yes",
+ "agama": "network.connections[].bond.ports"
+ },
+ {
+ "key": "bonding_slave6",
+ "support": "yes",
+ "agama": "network.connections[].bond.ports"
+ },
+ {
+ "key": "bonding_slave7",
+ "support": "yes",
+ "agama": "network.connections[].bond.ports"
+ },
+ {
+ "key": "bonding_slave8",
+ "support": "yes",
+ "agama": "network.connections[].bond.ports"
+ },
+ {
+ "key": "bonding_slave9",
+ "support": "yes",
+ "agama": "network.connections[].bond.ports"
+ },
+ { "key": "bridge", "support": "planned" },
+ { "key": "bridge_forwarddelay", "support": "planned" },
+ { "key": "bridge_ports", "support": "planned" },
+ { "key": "bridge_stp", "support": "planned" },
+ { "key": "vlan_id", "support": "planned" },
+ { "key": "wireless_auth_mode", "support": "yes" },
+ { "key": "wireless_ap", "support": "no" },
+ { "key": "wireless_bitrate", "support": "no" },
+ { "key": "wireless_ca_cert", "support": "no" },
+ { "key": "wireless_channel", "support": "no" },
+ { "key": "wireless_client_cert", "support": "no" },
+ { "key": "wireless_client_key", "support": "no" },
+ { "key": "wireless_client_key_password", "support": "no" },
+ { "key": "wireless_default_key", "support": "no" },
+ { "key": "wireless_eap_auth", "support": "no" },
+ { "key": "wireless_eap_mode", "support": "no" },
+ { "key": "wireless_essid", "support": "yes", "agama": "ssid" },
+ { "key": "wireless_frequency", "support": "no" },
+ { "key": "wireless_key", "support": "no" },
+ { "key": "wireless_key_0", "support": "no" },
+ { "key": "wireless_key_1", "support": "no" },
+ { "key": "wireless_key_2", "support": "no" },
+ { "key": "wireless_key_3", "support": "no" },
+ { "key": "wireless_key_length", "support": "no" },
+ { "key": "wireless_mode", "support": "yes", "agama": "mode" },
+ { "key": "wireless_nick", "support": "no" },
+ { "key": "wireless_nwid", "support": "no" },
+ { "key": "wireless_peap_version", "support": "no" },
+ { "key": "wireless_power", "support": "no" },
+ { "key": "wireless_wpa_anonid", "support": "no" },
+ { "key": "wireless_wpa_identity", "support": "no" },
+ {
+ "key": "wireless_wpa_password",
+ "support": "yes",
+ "agama": "password"
+ },
+ { "key": "wireless_wpa_psk", "support": "yes", "agama": "password" }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "services-manager",
+ "support": "planned",
+ "notes": "You can use a post-installation script to handle these cases."
+ },
+ {
+ "key": "scripts",
+ "children": [
+ {
+ "key": "pre-scripts[]",
+ "children": [
+ {
+ "key": "filename",
+ "support": "yes",
+ "agama": "scripts.pre[].name"
+ },
+ { "key": "location", "support": "yes", "agama": "scripts.pre[].url" },
+ { "key": "source", "support": "yes", "agama": "scripts.pre[].body" },
+ {
+ "key": "interpreter",
+ "support": "no",
+ "notes": "Use the shebang line in your scripts."
+ },
+ { "key": "feedback", "support": "no" },
+ { "key": "feedback_type", "support": "no" },
+ { "key": "debug", "support": "no" },
+ { "key": "notification", "support": "no" },
+ { "key": "param-list", "support": "no" },
+ { "key": "rerun", "support": "no" }
+ ]
+ },
+ {
+ "key": "chroot-scripts[]",
+ "agama": "scripts.post[]",
+ "children": [
+ {
+ "key": "filename",
+ "support": "yes",
+ "agama": "scripts.chroot[].name"
+ },
+ {
+ "key": "location",
+ "support": "yes",
+ "agama": "scripts.chroot[].url"
+ },
+ {
+ "key": "source",
+ "support": "yes",
+ "agama": "scripts.chroot[].body"
+ },
+ {
+ "key": "interpreter",
+ "support": "no",
+ "notes": "Use the shebang line in your scripts."
+ },
+ { "key": "feedback", "support": "no" },
+ { "key": "feedback_type", "support": "no" },
+ { "key": "debug", "support": "no" },
+ { "key": "notification", "support": "no" },
+ { "key": "param-list", "support": "no" },
+ { "key": "rerun", "support": "no" }
+ ]
+ },
+ {
+ "key": "post-scripts[]",
+ "agama": "scripts.init[]",
+ "children": [
+ {
+ "key": "filename",
+ "support": "yes",
+ "agama": "scripts.init[].name"
+ },
+ {
+ "key": "location",
+ "support": "yes",
+ "agama": "scripts.init[].url"
+ },
+ { "key": "source", "support": "yes", "agama": "scripts.init[].body" },
+ {
+ "key": "interpreter",
+ "support": "no",
+ "notes": "Use the shebang line in your scripts."
+ },
+ { "key": "feedback", "support": "no" },
+ { "key": "feedback_type", "support": "no" },
+ { "key": "debug", "support": "no" },
+ { "key": "notification", "support": "no" },
+ { "key": "param-list", "support": "no" },
+ { "key": "rerun", "support": "no" }
+ ]
+ },
+ {
+ "key": "init-scripts[]",
+ "agama": "scripts.init[]",
+ "children": [
+ {
+ "key": "filename",
+ "support": "yes",
+ "agama": "scripts.init[].name"
+ },
+ {
+ "key": "location",
+ "support": "yes",
+ "agama": "scripts.init[].url"
+ },
+ { "key": "source", "support": "yes", "agama": "scripts.init[].body" },
+ { "key": "rerun", "support": "no" }
+ ]
+ }
+ ]
+ },
+ { "key": "mail", "support": "no" },
+ { "key": "nfs", "support": "no" },
+ { "key": "nfs_server", "support": "no" },
+ { "key": "nis", "support": "no" },
+ { "key": "nis_server", "support": "no" },
+ { "key": "ntp-client", "support": "no" },
+ { "key": "printer", "support": "no" },
+ {
+ "key": "proxy",
+ "support": "planned",
+ "notes": "Set the proxy using the kernels' command line"
+ },
+ { "key": "report", "support": "no" },
+ { "key": "samba-client", "support": "no" },
+ {
+ "key": "software",
+ "children": [
+ { "key": "do_online_update", "support": "no", "notes": "No 2nd stage" },
+ { "key": "install_recommended", "support": "no" },
+ { "key": "instsource", "support": "no" },
+ { "key": "kernel", "support": "no" },
+ { "key": "packages[]", "support": "planned" },
+ { "key": "post-packages[]", "support": "no" },
+ { "key": "patterns[]", "support": "yes", "agama": "software.patterns[]" },
+ { "key": "products[]", "support": "yes", "agama": "software.id" },
+ { "key": "remove-packages[]", "support": "no" },
+ { "key": "remove-patterns[]", "support": "no" },
+ { "key": "remove-products[]", "support": "no" }
+ ]
+ },
+ { "key": "sound", "support": "no" },
+ { "key": "squid", "support": "no" },
+ { "key": "ssh_import", "support": "no" },
+ {
+ "key": "suse_register",
+ "children": [
+ {
+ "key": "do_registration",
+ "support": "yes",
+ "notes": "The while section is ignored if \"false\""
+ },
+ {
+ "key": "email",
+ "support": "yes",
+ "agama": "product.registrationEmail"
+ },
+ { "key": "install_updates", "support": "no" },
+ {
+ "key": "reg_code",
+ "support": "yes",
+ "agama": "product.registrationCode"
+ },
+ { "key": "reg_server", "support": "planned" },
+ { "key": "reg_server_cert", "support": "planned" },
+ { "key": "reg_server_cert_fingerprint", "support": "planned" },
+ { "key": "reg_server_cert_fingerprint_type", "support": "planned" },
+ { "key": "addons", "support": "planned" },
+ { "key": "slp_discovery", "support": "planned" }
+ ]
+ },
+ { "key": "sysconfig", "support": "no" },
+ { "key": "tftp-server", "support": "no" },
+ {
+ "key": "timezone",
+ "children": [
+ { "key": "timezone", "support": "yes", "agama": "localization.timezone" },
+ { "key": "hwclock", "support": "no" }
+ ]
+ },
+ { "key": "upgrade", "support": "no" },
+ { "key": "iscsi-client", "support": "planned" },
+ {
+ "key": "users[]",
+ "notes": "Only the root and the first user are considered.",
+ "children": [
+ { "key": "username", "support": "yes", "agama": "user.userName" },
+ { "key": "fullName", "support": "yes", "agama": "user.fullName" },
+ { "key": "password", "support": "yes", "agama": "user.password" },
+ {
+ "key": "encrypted",
+ "support": "yes",
+ "agama": "user.hashedPassword",
+ "notes": "If set to true, it uses \"hashedPassword\" instead of \"password\""
+ },
+ {
+ "key": "authorized_keys",
+ "support": "yes",
+ "agama": "root.sshPublicKey",
+ "notes": "It only considers one password and only for the root user."
+ }
+ ]
+ }
+]
diff --git a/service/share/autoyast-compat.schema.json b/service/share/autoyast-compat.schema.json
new file mode 100644
index 0000000000..adafe72989
--- /dev/null
+++ b/service/share/autoyast-compat.schema.json
@@ -0,0 +1,44 @@
+{
+ "$schema": "https://json-schema.org/draft-07/schema",
+ "$id": "https://github.com/openSUSE/agama/blob/master/service/share/autoyast-compat.schema.json",
+ "title": "AutoYaST profile compatibility description",
+ "description": "For elements of AutoYaST profiles, describes whether Agama will understand them",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/profileElement"
+ },
+ "definitions": {
+ "profileElement": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "support": {
+ "type": "string",
+ "enum": ["yes", "no", "planned"]
+ },
+ "notes": {
+ "type": "string"
+ },
+ "agama": {
+ "type": "string"
+ },
+ "children": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/profileElement"
+ }
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "oneOf": [
+ { "required": ["support"] },
+ { "required": ["children"] }
+ ]
+ }
+ }
+}
diff --git a/service/test/agama/autoyast/converter_test.rb b/service/test/agama/autoyast/converter_test.rb
index 44256439c5..57ab318ee8 100644
--- a/service/test/agama/autoyast/converter_test.rb
+++ b/service/test/agama/autoyast/converter_test.rb
@@ -20,120 +20,40 @@
# find current contact information at www.suse.com.
require_relative "../../test_helper"
+require "yast"
require "agama/autoyast/converter"
require "json"
require "tmpdir"
require "autoinstall/xml_checks"
require "y2storage"
+Yast.import "Profile"
+
describe Agama::AutoYaST::Converter do
- let(:profile) { File.join(FIXTURES_PATH, "profiles", profile_name) }
- let(:profile_name) { "simple.xml" }
- let(:workdir) { Dir.mktmpdir }
- let(:tmpdir) { Dir.mktmpdir }
- let(:xml_validator) do
- instance_double(
- Y2Autoinstallation::XmlValidator,
- valid?: xml_valid?,
- errors: xml_errors
- )
- end
- let(:xml_valid?) { true }
- let(:xml_errors) { [] }
- let(:result) do
- content = File.read(File.join(workdir, "autoinst.json"))
- JSON.parse(content)
- end
- let(:storage_manager) do
- instance_double(
- Y2Storage::StorageManager,
- probed: storage_probed,
- probed_disk_analyzer: disk_analyzer
+ let(:profile) do
+ Yast::Profile.ReadXML(
+ File.join(FIXTURES_PATH, "profiles", profile_name)
)
- end
- let(:storage_probed) do
- instance_double(Y2Storage::Devicegraph, disks: [])
- end
- let(:disk_analyzer) do
- instance_double(Y2Storage::DiskAnalyzer, windows_partitions: [], linux_partitions: [])
- end
-
- before do
- stub_const("Y2Autoinstallation::XmlChecks::ERRORS_PATH", File.join(tmpdir, "errors"))
- Yast.import "Installation"
- allow(Yast::Installation).to receive(:sourcedir).and_return(File.join(tmpdir, "mount"))
- allow(Yast::AutoinstConfig).to receive(:scripts_dir)
- .and_return(File.join(tmpdir, "scripts"))
- allow(Yast::AutoinstConfig).to receive(:profile_dir)
- .and_return(File.join(tmpdir, "profile"))
- allow(Yast::AutoinstConfig).to receive(:modified_profile)
- .and_return(File.join(tmpdir, "profile", "modified.xml"))
- allow(Y2Autoinstallation::XmlValidator).to receive(:new).and_return(xml_validator)
- allow(Y2Storage::StorageManager).to receive(:instance).and_return(storage_manager)
+ Yast::Profile.current
end
- after do
- FileUtils.remove_entry(workdir)
- FileUtils.remove_entry(tmpdir)
- end
+ let(:profile_name) { "simple.xml" }
subject do
- described_class.new("file://#{profile}")
+ described_class.new
end
describe "#to_agama" do
- context "when some pre-script is defined" do
- let(:profile_name) { "pre-scripts.xml" }
- let(:profile) { File.join(tmpdir, profile_name) }
-
- before do
- allow(Yast::AutoinstConfig).to receive(:scripts_dir)
- .and_return(File.join(tmpdir, "scripts"))
- allow(Yast::AutoinstConfig).to receive(:profile_dir)
- .and_return(File.join(tmpdir, "profile"))
-
- # Adapt the script to use the new tmp directory
- profile_content = File.read(File.join(FIXTURES_PATH, "profiles", profile_name))
- profile_content.gsub!("/tmp/profile/", "#{tmpdir}/profile/")
- File.write(profile, profile_content)
- end
-
- it "runs the script" do
- subject.to_agama(workdir)
- expect(result["product"]).to include("id" => "Tumbleweed")
- end
- end
-
- context "when the profile contains some ERB" do
- let(:profile_name) { "simple.xml.erb" }
-
- it "evaluates the ERB code" do
- subject.to_agama(workdir)
- expect(result["l10n"]).to include(
- "languages" => ["en_US.UTF-8", "es_ES.UTF-8"]
- )
- end
- end
-
- context "when the profile uses rules" do
- let(:profile_name) { "profile/" }
-
- it "evaluates the rules" do
- subject.to_agama(workdir)
- expect(result["product"]).to include("id" => "Tumbleweed")
- end
- end
-
context "when a product is selected" do
it "exports the selected product" do
- subject.to_agama(workdir)
+ result = subject.to_agama(profile)
expect(result["product"]).to include("id" => "Tumbleweed")
end
end
context "when partitioning is defined" do
it "exports the drives information" do
- subject.to_agama(workdir)
+ result = subject.to_agama(profile)
expect(result["legacyAutoyastStorage"]).to include({
"device" => "/dev/vda",
"use" => "all"
@@ -143,7 +63,7 @@
context "when the root password and/or public SSH key are set" do
it "exports the root password and/or public SSH key" do
- subject.to_agama(workdir)
+ result = subject.to_agama(profile)
expect(result["root"]).to include("password" => "nots3cr3t",
"sshPublicKey" => "ssh-rsa ...")
end
@@ -151,14 +71,14 @@
context "when a non-system user is defined" do
it "exports the user information" do
- subject.to_agama(workdir)
+ result = subject.to_agama(profile)
expect(result["user"]).to include("userName" => "jane",
"password" => "12345678", "fullName" => "Jane Doe")
end
end
it "exports l10n settings" do
- subject.to_agama(workdir)
+ result = subject.to_agama(profile)
expect(result["l10n"]).to include(
"languages" => ["en_US.UTF-8"],
"timezone" => "Atlantic/Canary",
@@ -166,15 +86,4 @@
)
end
end
-
- context "when an invalid profile is given" do
- let(:xml_valid?) { false }
- let(:xml_errors) { ["Some validation error"] }
- let(:profile_name) { "invalid.xml" }
-
- it "reports the problem" do
- expect(Yast2::Popup).to receive(:show)
- subject.to_agama(workdir)
- end
- end
end
diff --git a/service/test/agama/autoyast/profile_checker_test.rb b/service/test/agama/autoyast/profile_checker_test.rb
new file mode 100644
index 0000000000..aeee0ec21b
--- /dev/null
+++ b/service/test/agama/autoyast/profile_checker_test.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2025] 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_relative "../../test_helper"
+require "agama/autoyast/profile_checker"
+
+describe Agama::AutoYaST::ProfileChecker do
+ describe "#find_unsupported" do
+ context "when no unsupported elements are included" do
+ let(:profile) do
+ { "software" => {} }
+ end
+
+ it "returns an empty array" do
+ expect(subject.find_unsupported(profile)).to eq([])
+ end
+ end
+
+ context "when an unsupported section is included" do
+ let(:profile) do
+ { "iscsi-client" => {} }
+ end
+
+ it "returns an array with the unsupported element" do
+ expect(subject.find_unsupported(profile)).to contain_exactly(
+ an_object_having_attributes(key: "iscsi-client")
+ )
+ end
+ end
+
+ context "when an unsupported element is included" do
+ let(:profile) do
+ { "networking" => { "backend" => "wicked" } }
+ end
+
+ it "returns an array with the unsupported element" do
+ expect(subject.find_unsupported(profile)).to contain_exactly(
+ an_object_having_attributes(key: "networking/backend")
+ )
+ end
+ end
+
+ context "when an unsupported nested element is included" do
+ let(:profile) do
+ {
+ "scripts" => {
+ "pre-scripts" => [
+ { "location" => "http://example.net/pre-script.sh",
+ "rerun" => true }
+ ]
+ }
+ }
+ end
+
+ it "returns an array with the unsupported element" do
+ expect(subject.find_unsupported(profile)).to contain_exactly(
+ an_object_having_attributes(key: "scripts/pre-scripts[]/rerun")
+ )
+ end
+ end
+ end
+end
diff --git a/service/test/agama/autoyast/profile_description_test.rb b/service/test/agama/autoyast/profile_description_test.rb
new file mode 100644
index 0000000000..4b8af078c1
--- /dev/null
+++ b/service/test/agama/autoyast/profile_description_test.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2025] 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_relative "../../test_helper"
+require "agama/autoyast/profile_description"
+
+describe Agama::AutoYaST::ProfileDescription do
+ let(:subject) { described_class.load }
+
+ describe "#find_element" do
+ context "when the element exists" do
+ it "returns the element data" do
+ element = subject.find_element("networking/backend")
+ expect(element.key).to eq("networking/backend")
+ expect(element.support).to eq(:no)
+ end
+ end
+
+ context "when the element is unknown" do
+ it "returns nil" do
+ expect(subject.find_element("iscsi-client/dummy")).to be_nil
+ end
+ end
+ end
+end
diff --git a/service/test/agama/autoyast/profile_fetcher_test.rb b/service/test/agama/autoyast/profile_fetcher_test.rb
new file mode 100644
index 0000000000..15357d60bc
--- /dev/null
+++ b/service/test/agama/autoyast/profile_fetcher_test.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2025] 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_relative "../../test_helper"
+require "agama/autoyast/profile_fetcher"
+require "json"
+require "tmpdir"
+require "autoinstall/xml_checks"
+require "y2storage"
+
+Yast.import "ProfileLocation"
+
+describe Agama::AutoYaST::ProfileFetcher do
+ let(:profile) { File.join(FIXTURES_PATH, "profiles", profile_name) }
+ let(:profile_name) { "simple.xml" }
+ let(:tmpdir) { Dir.mktmpdir }
+ let(:xml_validator) do
+ instance_double(
+ Y2Autoinstallation::XmlValidator,
+ valid?: xml_valid?,
+ errors: xml_errors
+ )
+ end
+ let(:xml_valid?) { true }
+ let(:xml_errors) { [] }
+ let(:storage_manager) do
+ instance_double(
+ Y2Storage::StorageManager,
+ probed: storage_probed,
+ probed_disk_analyzer: disk_analyzer
+ )
+ end
+ let(:storage_probed) do
+ instance_double(Y2Storage::Devicegraph, disks: [])
+ end
+ let(:disk_analyzer) do
+ instance_double(Y2Storage::DiskAnalyzer, windows_partitions: [], linux_partitions: [])
+ end
+
+ before do
+ stub_const("Y2Autoinstallation::XmlChecks::ERRORS_PATH", File.join(tmpdir, "errors"))
+ Yast.import "Installation"
+ allow(Yast::Installation).to receive(:sourcedir).and_return(File.join(tmpdir, "mount"))
+ allow(Yast::AutoinstConfig).to receive(:scripts_dir)
+ .and_return(File.join(tmpdir, "scripts"))
+ allow(Yast::AutoinstConfig).to receive(:profile_dir)
+ .and_return(File.join(tmpdir, "profile"))
+ allow(Yast::AutoinstConfig).to receive(:modified_profile)
+ .and_return(File.join(tmpdir, "profile", "modified.xml"))
+ allow(Y2Autoinstallation::XmlValidator).to receive(:new).and_return(xml_validator)
+ allow(Y2Storage::StorageManager).to receive(:instance).and_return(storage_manager)
+ end
+
+ after do
+ FileUtils.remove_entry(tmpdir)
+ end
+
+ subject do
+ described_class.new("file://#{profile}")
+ end
+
+ describe "#fetch" do
+ context "when some pre-script is defined" do
+ let(:profile_name) { "pre-scripts.xml" }
+ let(:profile) { File.join(tmpdir, profile_name) }
+
+ before do
+ allow(Yast::AutoinstConfig).to receive(:scripts_dir)
+ .and_return(File.join(tmpdir, "scripts"))
+ allow(Yast::AutoinstConfig).to receive(:profile_dir)
+ .and_return(File.join(tmpdir, "profile"))
+
+ # Adapt the script to use the new tmp directory
+ profile_content = File.read(File.join(FIXTURES_PATH, "profiles", profile_name))
+ profile_content.gsub!("/tmp/profile/", "#{tmpdir}/profile/")
+ File.write(profile, profile_content)
+ end
+
+ it "runs the script" do
+ result = subject.fetch
+ expect(result["software"]).to include("products" => ["Tumbleweed"])
+ end
+ end
+
+ context "when the profile contains some ERB" do
+ let(:profile_name) { "simple.xml.erb" }
+
+ it "evaluates the ERB code" do
+ result = subject.fetch
+ expect(result["language"]).to include(
+ "language" => "en_US"
+ )
+ end
+ end
+
+ context "when the profile uses rules" do
+ let(:profile_name) { "profile/" }
+
+ it "evaluates the rules" do
+ result = subject.fetch
+ expect(result["software"]).to include("products" => ["Tumbleweed"])
+ end
+ end
+
+ context "when it cannot download the profile" do
+ before do
+ allow(Yast::ProfileLocation).to receive(:Process).and_return(false)
+ end
+
+ it "returns nil" do
+ expect(subject.fetch).to be_nil
+ end
+
+ it "does not process the profile" do
+ expect(Yast::Profile).to_not receive(:ReadXML)
+ subject.fetch
+ end
+ end
+ end
+
+ context "when an invalid profile is given" do
+ let(:xml_valid?) { false }
+ let(:xml_errors) { ["Some validation error"] }
+ let(:profile_name) { "invalid.xml" }
+
+ it "reports the problem" do
+ expect(Yast2::Popup).to receive(:show)
+ subject.fetch
+ end
+ end
+end
diff --git a/service/test/agama/commands/agama_autoyast_test.rb b/service/test/agama/commands/agama_autoyast_test.rb
new file mode 100644
index 0000000000..eb0c843c69
--- /dev/null
+++ b/service/test/agama/commands/agama_autoyast_test.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2025] 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_relative "../../test_helper"
+require "agama/commands/agama_autoyast"
+require "yast"
+Yast.import "Profile"
+
+describe Agama::Commands::AgamaAutoYaST do
+ subject { described_class.new(url, tmpdir) }
+
+ let(:fetcher) { instance_double(Agama::AutoYaST::ProfileFetcher) }
+ let(:checker) { Agama::AutoYaST::ProfileChecker.new }
+ let(:reporter) { instance_double(Agama::AutoYaST::ProfileReporter, report: true) }
+ let(:questions) { instance_double(Agama::DBus::Clients::Questions) }
+ let(:profile) do
+ Yast::ProfileHash.new({ "software" => { "products" => ["openSUSE"] } })
+ end
+ let(:url) { "http://example.net/autoyast.xml" }
+ let(:tmpdir) { Dir.mktmpdir }
+ let(:cmdline_args) { Agama::CmdlineArgs.new({}) }
+
+ before do
+ allow(Agama::AutoYaST::ProfileFetcher).to receive(:new).with(url).and_return(fetcher)
+ allow(Agama::AutoYaST::ProfileChecker).to receive(:new).and_return(checker)
+ allow(Agama::AutoYaST::ProfileReporter).to receive(:new).and_return(reporter)
+ allow(Agama::DBus::Clients::Questions).to receive(:new).and_return(questions)
+ allow(Agama::CmdlineArgs).to receive(:read_from).and_return(cmdline_args)
+ allow(fetcher).to receive(:fetch).and_return(profile)
+ end
+
+ after do
+ FileUtils.remove_entry(tmpdir)
+ end
+
+ describe "#run" do
+ it "checks for unsupported elements" do
+ expect(checker).to receive(:find_unsupported).with(profile)
+ .and_call_original
+ subject.run
+ end
+
+ it "writes the Agama equivalent to the given directory" do
+ subject.run
+ autoinst = File.read(File.join(tmpdir, "autoinst.json"))
+ expect(autoinst).to include("openSUSE")
+ end
+
+ context "when the profile includes unsupported elements" do
+ let(:profile) do
+ Yast::ProfileHash.new({ "networking" => { "backend" => "wicked" } })
+ end
+
+ it "reports them to the user" do
+ expect(reporter).to receive(:report).and_return(true)
+ subject.run
+ end
+
+ context "but the error reporting is disabled" do
+ let(:cmdline_args) { Agama::CmdlineArgs.new({ "ay_check" => "0" }) }
+
+ it "does not report the errors" do
+ expect(reporter).to_not receive(:report)
+ subject.run
+ end
+ end
+ end
+
+ context "when the profile could not be fetched" do
+ before do
+ allow(fetcher).to receive(:fetch).and_raise("Could not fetch the profile")
+ end
+
+ it "raises a CouldNotFetchProfile exception" do
+ expect { subject.run }.to raise_exception(Agama::Commands::CouldNotFetchProfile)
+ end
+ end
+ end
+end
diff --git a/service/test/agama/software/callbacks/provide_test.rb b/service/test/agama/software/callbacks/provide_test.rb
new file mode 100644
index 0000000000..03146c34cb
--- /dev/null
+++ b/service/test/agama/software/callbacks/provide_test.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2022-2023] 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_relative "../../../test_helper"
+require "agama/software/callbacks/provide"
+require "agama/dbus/clients/questions"
+require "agama/dbus/clients/question"
+
+describe Agama::Software::Callbacks::Provide do
+ subject { described_class.new(questions_client, logger) }
+
+ let(:questions_client) { instance_double(Agama::DBus::Clients::Questions) }
+
+ let(:logger) { Logger.new($stdout, level: :warn) }
+
+ let(:answer) { :Retry }
+
+ describe "#done_provide" do
+ before do
+ allow(questions_client).to receive(:ask).and_yield(question_client)
+ allow(question_client).to receive(:answer).and_return(answer)
+ end
+
+ let(:question_client) { instance_double(Agama::DBus::Clients::Question) }
+
+ context "when the file is not found" do
+ it "does not register a question" do
+ expect(questions_client).to_not receive(:ask)
+ subject.done_provide(1, "Some dummy reason", "dummy-package")
+ end
+ end
+
+ context "when the there is an I/O error" do
+ it "registers a question informing of the error" do
+ expect(questions_client).to receive(:ask) do |q|
+ expect(q.text).to include("could not be downloaded")
+ end
+ subject.done_provide(2, "Some dummy reason", "dummy-package")
+ end
+ end
+
+ context "when the there is an I/O error" do
+ it "registers a question informing of the error" do
+ expect(questions_client).to receive(:ask) do |q|
+ expect(q.text).to include("integrity check has failed")
+ end
+ subject.done_provide(3, "Some dummy reason", "dummy-package")
+ end
+ end
+
+ context "when the user answers :Retry" do
+ let(:answer) { :Retry }
+
+ it "returns 'R'" do
+ ret = subject.done_provide(
+ 2, "Some dummy reason", "dummy-package"
+ )
+ expect(ret).to eq("R")
+ end
+ end
+
+ context "when the user answers :Skip" do
+ let(:answer) { :Ignore }
+
+ it "returns 'I'" do
+ ret = subject.done_provide(
+ 2, "Some dummy reason", "dummy-package"
+ )
+ expect(ret).to eq("I")
+ end
+ end
+ end
+end
diff --git a/service/test/agama/software/callbacks/script_test.rb b/service/test/agama/software/callbacks/script_test.rb
new file mode 100644
index 0000000000..95a596820f
--- /dev/null
+++ b/service/test/agama/software/callbacks/script_test.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+# Copyright (c) [2022-2023] 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_relative "../../../test_helper"
+require "agama/software/callbacks/script"
+require "agama/dbus/clients/questions"
+require "agama/dbus/clients/question"
+
+describe Agama::Software::Callbacks::Script do
+ subject { described_class.new(questions_client, logger) }
+
+ let(:questions_client) { instance_double(Agama::DBus::Clients::Questions) }
+
+ let(:logger) { Logger.new($stdout, level: :warn) }
+
+ let(:answer) { :Retry }
+
+ let(:description) { "Some description" }
+
+ describe "#script_problem" do
+ before do
+ allow(questions_client).to receive(:ask).and_yield(question_client)
+ allow(question_client).to receive(:answer).and_return(answer)
+ end
+
+ let(:question_client) { instance_double(Agama::DBus::Clients::Question) }
+
+ it "registers a question with the details" do
+ expect(questions_client).to receive(:ask) do |q|
+ expect(q.text).to include("running a package script")
+ expect(q.data).to include(
+ "details" => description
+ )
+ end
+ subject.script_problem(description)
+ end
+
+ context "when the user answers :Retry" do
+ let(:answer) { :Retry }
+
+ it "returns 'R'" do
+ ret = subject.script_problem(description)
+ expect(ret).to eq("R")
+ end
+ end
+
+ context "when the user answers :Ignore" do
+ let(:answer) { :Ignore }
+
+ it "returns 'I'" do
+ ret = subject.script_problem(description)
+ expect(ret).to eq("I")
+ end
+ end
+ end
+end
diff --git a/service/test/agama/software/manager_test.rb b/service/test/agama/software/manager_test.rb
index e5583c017c..f3551e9429 100644
--- a/service/test/agama/software/manager_test.rb
+++ b/service/test/agama/software/manager_test.rb
@@ -89,6 +89,7 @@
allow(Yast::Pkg).to receive(:TargetFinish)
allow(Yast::Pkg).to receive(:TargetLoad)
allow(Yast::Pkg).to receive(:SourceSaveAll)
+ allow(Yast::Pkg).to receive(:SourceDelete)
allow(Yast::Pkg).to receive(:ImportGPGKey)
# allow glob to work for other calls
allow(Dir).to receive(:glob).and_call_original
@@ -383,6 +384,15 @@
end
it "copies the libzypp cache and credentials to the target system" do
+ allow(Agama::Software::Repository).to receive(:all).and_return(
+ [
+ Agama::Software::Repository.new(
+ repo_id: 42, repo_alias: "alias", name: "name",
+ url: "http://example.com", enabled: true, autorefresh: false
+ )
+ ]
+ )
+
allow(Dir).to receive(:exist?).and_call_original
allow(Dir).to receive(:entries).and_call_original
@@ -435,6 +445,34 @@
subject.finish
end
+
+ context "only a local repository is used" do
+ let(:repo_id) { 42 }
+ before do
+ expect(Agama::Software::Repository).to receive(:all).and_return(
+ [
+ Agama::Software::Repository.new(
+ repo_id: repo_id, repo_alias: "alias", name: "name",
+ url: "dir:///run/initramfs/live/install", enabled: true, autorefresh: false
+ )
+ ],
+ # for the second and further calls return empty list, the repo has been removed
+ []
+ )
+ end
+
+ it "removes the local repository" do
+ expect(Yast::Pkg).to receive(:SourceDelete).with(repo_id)
+
+ subject.finish
+ end
+
+ it "does not copy the libzypp cache" do
+ expect(subject).to_not receive(:copy_zypp_to_target)
+
+ subject.finish
+ end
+ end
end
describe "#package_installed?" do
diff --git a/service/test/agama/software/repositories_manager_test.rb b/service/test/agama/software/repositories_manager_test.rb
index 4a5983d369..4930b9ad46 100644
--- a/service/test/agama/software/repositories_manager_test.rb
+++ b/service/test/agama/software/repositories_manager_test.rb
@@ -23,9 +23,10 @@
require "agama/software/repositories_manager"
describe Agama::Software::RepositoriesManager do
+ # probe and refresh succeed
let(:repo) do
instance_double(
- Agama::Software::Repository, enable!: nil, probe: true, enabled?: true, refresh: false
+ Agama::Software::Repository, enable!: nil, probe: true, enabled?: true, refresh: true
)
end
@@ -45,28 +46,43 @@
end
describe "#load" do
+ # probe and refresh fail
let(:repo1) do
instance_double(
- Agama::Software::Repository, disable!: nil, probe: false, refresh: false
+ Agama::Software::Repository, enable!: nil, disable!: nil, probe: false, refresh: false
+ )
+ end
+
+ # corner case, probe succeeds but refresh fails
+ let(:repo2) do
+ instance_double(
+ Agama::Software::Repository, enable!: nil, disable!: nil, probe: true, refresh: false
)
end
before do
subject.repositories << repo
subject.repositories << repo1
+ subject.repositories << repo2
allow(Yast::Pkg).to receive(:SourceLoad)
end
it "enables the repositories that can be read" do
expect(repo).to receive(:enable!)
+ expect(repo).to_not receive(:disable!)
subject.load
end
- it "disables the repositories that cannot be read" do
+ it "disables the repositories that cannot be probed" do
expect(repo1).to receive(:disable!)
subject.load
end
+ it "disables the repositories that cannot be refreshed" do
+ expect(repo2).to receive(:disable!)
+ subject.load
+ end
+
it "loads the repositories" do
expect(Yast::Pkg).to receive(:SourceLoad)
subject.load
diff --git a/web/package/agama-web-ui.changes b/web/package/agama-web-ui.changes
index 39d67e2bc8..e707b5b0ed 100644
--- a/web/package/agama-web-ui.changes
+++ b/web/package/agama-web-ui.changes
@@ -1,3 +1,16 @@
+-------------------------------------------------------------------
+Fri Feb 7 14:43:08 UTC 2025 - Imobach Gonzalez Sosa
+
+- Report unsupported AutoYaST elements
+ (gh#agama-project/agama#1976).
+
+-------------------------------------------------------------------
+Fri Jan 31 09:03:37 UTC 2025 - Imobach Gonzalez Sosa
+
+- Always perform mutations (PATH/POST/PUT/DELETE) when connecting
+ to "localhost", no matter whether the network is connected or not
+ (gh#agama-project/agama#1963).
+
-------------------------------------------------------------------
Thu Jan 30 12:04:23 UTC 2025 - Martin Vidner
diff --git a/web/src/components/questions/Questions.tsx b/web/src/components/questions/Questions.tsx
index d96b409f97..a8cbb47706 100644
--- a/web/src/components/questions/Questions.tsx
+++ b/web/src/components/questions/Questions.tsx
@@ -24,6 +24,7 @@ import React from "react";
import GenericQuestion from "~/components/questions/GenericQuestion";
import QuestionWithPassword from "~/components/questions/QuestionWithPassword";
import LuksActivationQuestion from "~/components/questions/LuksActivationQuestion";
+import UnsupportedAutoYaST from "~/components/questions/UnsupportedAutoYaST";
import { useQuestions, useQuestionsConfig, useQuestionsChanges } from "~/queries/questions";
import { AnswerCallback, QuestionType } from "~/types/questions";
@@ -53,5 +54,9 @@ export default function Questions(): React.ReactNode {
QuestionComponent = LuksActivationQuestion;
}
+ if (currentQuestion.class === "autoyast.unsupported") {
+ QuestionComponent = UnsupportedAutoYaST;
+ }
+
return ;
}
diff --git a/web/src/components/questions/UnsupportedAutoYaST.test.tsx b/web/src/components/questions/UnsupportedAutoYaST.test.tsx
new file mode 100644
index 0000000000..1f9845b9ee
--- /dev/null
+++ b/web/src/components/questions/UnsupportedAutoYaST.test.tsx
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) [2025] SUSE LLC
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * 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.
+ */
+
+import React from "react";
+import { screen, within } from "@testing-library/react";
+import { AnswerCallback, Question } from "~/types/questions";
+import UnsupportedAutoYaST from "~/components/questions/UnsupportedAutoYaST";
+import { plainRender } from "~/test-utils";
+
+const question: Question = {
+ id: 1,
+ class: "autoyast.unsupported",
+ text: "Some elements from the AutoYaST profile are not supported.",
+ options: ["abort", "continue"],
+ defaultOption: "continue",
+ data: {
+ unsupported: "dns-server",
+ planned: "iscsi-client",
+ },
+};
+
+let mockQuestion = question;
+
+const answerFn: AnswerCallback = jest.fn();
+
+describe("UnsupportedAutoYaST", () => {
+ beforeEach(() => {
+ mockQuestion = { ...question };
+ });
+
+ it("mentions the planned elements", () => {
+ plainRender();
+
+ const list = screen.getByRole("region", { name: "Not implemented yet (1)" });
+ within(list).getByText("iscsi-client");
+ });
+
+ it("mentions the unsuported elements", () => {
+ plainRender();
+
+ const list = screen.getByRole("region", { name: "Not supported (1)" });
+ within(list).getByText("dns-server");
+ });
+
+ describe("when there are no unsupported (but planned) elements", () => {
+ beforeEach(() => {
+ mockQuestion = { ...question, data: {} };
+ });
+
+ it('does not render the "Not implemented yet" list', () => {
+ plainRender();
+
+ expect(screen.queryByRole("region", { name: /Not implemented/ })).not.toBeInTheDocument();
+ });
+ });
+
+ describe("when there are no unsupported (but planned) elements", () => {
+ beforeEach(() => {
+ mockQuestion = { ...question, data: {} };
+ });
+
+ it('does not render the "Not supported" list', () => {
+ plainRender();
+
+ expect(screen.queryByRole("region", { name: /Not supported/ })).not.toBeInTheDocument();
+ });
+ });
+});
diff --git a/web/src/components/questions/UnsupportedAutoYaST.tsx b/web/src/components/questions/UnsupportedAutoYaST.tsx
new file mode 100644
index 0000000000..b162137f26
--- /dev/null
+++ b/web/src/components/questions/UnsupportedAutoYaST.tsx
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) [2025] SUSE LLC
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * 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.
+ */
+
+import React from "react";
+import { Flex, Grid, GridItem, Content } from "@patternfly/react-core";
+import { AnswerCallback, Question } from "~/types/questions";
+import { Page, Popup } from "~/components/core";
+import { _ } from "~/i18n";
+import QuestionActions from "~/components/questions/QuestionActions";
+import { sprintf } from "sprintf-js";
+
+const UnsupportedElements = ({
+ elements,
+ title,
+ description,
+}: {
+ elements: string[];
+ title: string;
+ description: string;
+}) => {
+ if (elements.length === 0) {
+ return undefined;
+ }
+
+ return (
+
+
+
+ {elements.map((e: string, i: number) => (
+
+ {e}
+
+ ))}
+
+
+
+ );
+};
+
+export default function UnsupportedAutoYaST({
+ question,
+ answerCallback,
+}: {
+ question: Question;
+ answerCallback: AnswerCallback;
+}) {
+ const actionCallback = (option: string) => {
+ question.answer = option;
+ answerCallback(question);
+ };
+
+ const planned = question.data.planned?.split(",") || [];
+ const unsupported = question.data.unsupported?.split(",") || [];
+
+ return (
+
+ {_("Some of the elements in your AutoYaST profile are not supported.")}
+
+
+
+
+
+ {_(
+ 'If you want to disable this check, please specify "agama.ay_check=0" at kernel\'s command-line',
+ )}
+
+
+
+
+
+ );
+}
diff --git a/web/src/context/app.tsx b/web/src/context/app.tsx
index 339450901f..4bf3cb2646 100644
--- a/web/src/context/app.tsx
+++ b/web/src/context/app.tsx
@@ -39,11 +39,14 @@ const networkMode = (): "always" | "online" => {
return localConnection() ? "always" : "online";
};
+const sharedOptions = {
+ networkMode: networkMode(),
+};
+
const queryClient = new QueryClient({
defaultOptions: {
- queries: {
- networkMode: networkMode(),
- },
+ queries: sharedOptions,
+ mutations: sharedOptions,
},
});
diff --git a/web/src/po/po.ca.js b/web/src/po/po.ca.js
index 2d98d5a368..9f7852cb0e 100644
--- a/web/src/po/po.ca.js
+++ b/web/src/po/po.ca.js
@@ -564,6 +564,9 @@ export default {
"Hide details": [
"Amaga els detalls"
],
+ "I have read and accept the [license] for %s": [
+ "He llegit i accepto la [llicència] de %s"
+ ],
"IP Address": [
"Adreça IP"
],
@@ -693,6 +696,9 @@ export default {
"Language": [
"Llengua"
],
+ "License language": [
+ "Llengua de la llicència"
+ ],
"Limits for the file system size. The final size will be a value between the given minimum and maximum. If no maximum is given then the file system will be as big as possible.": [
"Límits per a la mida del sistema de fitxers. La mida final serà un valor entre el mínim i el màxim proporcionats. Si no hi ha cap màxim, el sistema de fitxers serà el més gros possible."
],
@@ -702,6 +708,9 @@ export default {
"Loading installation environment, please wait.": [
"Carregant l'entorn d'instal·lació. Espereu, si us plau."
],
+ "Loading the installation repositories...": [
+ "Carregant els repositoris d'instal·lació..."
+ ],
"Locale selection": [
"Selecció de la llengua"
],
@@ -1044,6 +1053,9 @@ export default {
"Remove min channel filter": [
"Suprimeix el filtre del canal mínim"
],
+ "Repository load failed": [
+ "Ha fallat carregar el repositori."
+ ],
"Reset location": [
"Restableix la ubicació"
],
@@ -1177,6 +1189,9 @@ export default {
"Software selection": [
"Selecció de programari"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ "Alguns repositoris d'instal·lació no s'han pogut carregar. El sistema no es pot instal·lar sense."
+ ],
"Something went wrong": [
"Alguna cosa ha anat malament."
],
@@ -1360,6 +1375,9 @@ export default {
"Transactional root file system": [
"Sistema de fitxers d'arrel transaccional"
],
+ "Try again": [
+ "Torna-ho a provar"
+ ],
"Type": [
"Tipus"
],
diff --git a/web/src/po/po.cs.js b/web/src/po/po.cs.js
index 22189c6cd7..4c1af5fa61 100644
--- a/web/src/po/po.cs.js
+++ b/web/src/po/po.cs.js
@@ -567,6 +567,9 @@ export default {
"Hide details": [
"Skrýt podrobnosti"
],
+ "I have read and accept the [license] for %s": [
+ ""
+ ],
"IP Address": [
"IP adresa"
],
@@ -1029,6 +1032,9 @@ export default {
"Remove min channel filter": [
"Odstranění filtru min. kanálu"
],
+ "Repository load failed": [
+ ""
+ ],
"Reset location": [
"Výmaz umístění"
],
@@ -1160,6 +1166,9 @@ export default {
"Software selection": [
"Výběr softwaru"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ ""
+ ],
"Something went wrong": [
"Něco se nezdařilo"
],
@@ -1343,6 +1352,9 @@ export default {
"Transactional root file system": [
"Transakční kořenový souborový systém"
],
+ "Try again": [
+ ""
+ ],
"Type": [
"Typ"
],
diff --git a/web/src/po/po.de.js b/web/src/po/po.de.js
index 2b9d0862ac..2757851f47 100644
--- a/web/src/po/po.de.js
+++ b/web/src/po/po.de.js
@@ -522,6 +522,9 @@ export default {
"Hide details": [
"Details ausblenden"
],
+ "I have read and accept the [license] for %s": [
+ ""
+ ],
"IP Address": [
"IP-Adresse"
],
@@ -966,6 +969,9 @@ export default {
"Remove min channel filter": [
""
],
+ "Repository load failed": [
+ ""
+ ],
"Reset location": [
"Ort zurücksetzen"
],
@@ -1096,6 +1102,9 @@ export default {
"Software selection": [
"Softwareauswahl"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ ""
+ ],
"Something went wrong": [
"Etwas ist schiefgelaufen"
],
diff --git a/web/src/po/po.es.js b/web/src/po/po.es.js
index 095fdbfdee..33f0bf220b 100644
--- a/web/src/po/po.es.js
+++ b/web/src/po/po.es.js
@@ -561,6 +561,9 @@ export default {
"Hide details": [
"Ocultar detalles"
],
+ "I have read and accept the [license] for %s": [
+ ""
+ ],
"IP Address": [
"Dirección IP"
],
@@ -1029,6 +1032,9 @@ export default {
"Remove min channel filter": [
"Eliminar filtro de canal mínimo"
],
+ "Repository load failed": [
+ ""
+ ],
"Reset location": [
"Reiniciar localización"
],
@@ -1159,6 +1165,9 @@ export default {
"Software selection": [
"Selección de software"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ ""
+ ],
"Something went wrong": [
"Algo salió mal"
],
diff --git a/web/src/po/po.fr.js b/web/src/po/po.fr.js
index f54d2f710f..f415338c07 100644
--- a/web/src/po/po.fr.js
+++ b/web/src/po/po.fr.js
@@ -473,6 +473,9 @@ export default {
"Hide details": [
"Masquer les détails"
],
+ "I have read and accept the [license] for %s": [
+ ""
+ ],
"IP Address": [
"Adresse IP"
],
@@ -896,6 +899,9 @@ export default {
"Remove min channel filter": [
"Supprimer le filtre canal minimal"
],
+ "Repository load failed": [
+ ""
+ ],
"Reset location": [
"Réinitialiser la localisation"
],
@@ -1002,6 +1008,9 @@ export default {
"Software %s": [
"Logiciel %s"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ ""
+ ],
"Something went wrong": [
"Quelque chose n'a pas fonctionné"
],
diff --git a/web/src/po/po.id.js b/web/src/po/po.id.js
index 95c12a2777..d9ef2f4a78 100644
--- a/web/src/po/po.id.js
+++ b/web/src/po/po.id.js
@@ -34,7 +34,7 @@ export default {
"%s diska"
],
"%s has been registered with below information.": [
- ""
+ "%s telah terdaftar dengan informasi di bawah ini."
],
"%s is an immutable system with atomic updates. It uses a read-only Btrfs file system updated via snapshots.": [
"%s adalah sistem yang tidak dapat diubah (immutable) dan mendukung pembaruan atomik. Sistem ini menggunakan file system Btrfs yang hanya-baca dan diperbarui melalui snapshot."
@@ -42,11 +42,14 @@ export default {
"%s logo": [
"logo %s"
],
+ "%s must be registered.": [
+ "%s harus didaftarkan."
+ ],
"%s with %d partitions": [
"%s dengan partisi %d"
],
"(optional)": [
- ""
+ "(opsional)"
],
", ": [
", "
@@ -460,7 +463,7 @@ export default {
"Kata Sandi Enkripsi"
],
"Enter a registration code and optionally a valid email address for registering the product.": [
- ""
+ "Masukkan kode registrasi dan alamat email yang valid untuk mendaftarkan produk."
],
"Exact size": [
"Ukuran yang tepat"
@@ -550,7 +553,7 @@ export default {
"GiB"
],
"Hide": [
- ""
+ "Sembunyikan"
],
"Hide %d subvolume action": [
"Menyembunyikan tindakan subvolume %d"
@@ -558,6 +561,9 @@ export default {
"Hide details": [
"Sembunyikan detail"
],
+ "I have read and accept the [license] for %s": [
+ "Saya telah membaca dan menerima [lisensi] untuk %s"
+ ],
"IP Address": [
"Alamat IP"
],
@@ -624,6 +630,9 @@ export default {
"Install new system on": [
"Instal sistem baru pada"
],
+ "Install using an advanced configuration.": [
+ "Instal menggunakan konfigurasi lanjutan."
+ ],
"Install using device %s and deleting all its content": [
"Menginstal menggunakan perangkat %s dan menghapus semua isinya"
],
@@ -684,6 +693,9 @@ export default {
"Language": [
"Bahasa"
],
+ "License language": [
+ "Bahasa lisensi"
+ ],
"Limits for the file system size. The final size will be a value between the given minimum and maximum. If no maximum is given then the file system will be as big as possible.": [
"Batas untuk ukuran sistem berkas. Ukuran akhir akan berupa nilai antara minimum dan maksimum yang diberikan. Jika tidak ada nilai maksimum yang diberikan, maka sistem berkas akan sebesar mungkin."
],
@@ -693,6 +705,9 @@ export default {
"Loading installation environment, please wait.": [
"Memuat lingkungan penginstalan, harap tunggu."
],
+ "Loading the installation repositories...": [
+ "Memuat repositori instalasi..."
+ ],
"Locale selection": [
"Pemilihan lokasi"
],
@@ -987,6 +1002,12 @@ export default {
"Presence of other volumes (%s)": [
"Keberadaan volume lain (%s)"
],
+ "Product registered": [
+ "Produk terdaftar"
+ ],
+ "Product registration form": [
+ "Formulir pendaftaran produk"
+ ],
"Protection for the information stored at the device, including data, programs, and system files.": [
"Perlindungan untuk informasi yang tersimpan di perangkat, termasuk data, program, dan file sistem."
],
@@ -1008,6 +1029,9 @@ export default {
"Register": [
"Mendaftar"
],
+ "Register it now": [
+ "Daftarkan sekarang"
+ ],
"Registration": [
"Pendaftaran"
],
@@ -1026,6 +1050,9 @@ export default {
"Remove min channel filter": [
"Menghapus filter saluran min"
],
+ "Repository load failed": [
+ "Pemuatan repositori gagal"
+ ],
"Reset location": [
"Atur ulang lokasi"
],
@@ -1119,6 +1146,9 @@ export default {
"Setup root user authentication": [
"Menyiapkan autentikasi pengguna root"
],
+ "Show": [
+ "Tampilkan"
+ ],
"Show %d subvolume action": [
"Tampilkan tindakan subvolume %d"
],
@@ -1155,6 +1185,9 @@ export default {
"Software selection": [
"Pemilihan perangkat lunak"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ "Beberapa repositori instalasi tidak dapat dimuat. Sistem tidak dapat diinstal tanpa repositori tersebut."
+ ],
"Something went wrong": [
"Ada yang tidak beres"
],
@@ -1282,7 +1315,7 @@ export default {
"Sistem belum dikonfigurasi untuk menghubungkan ke jaringan Wi-Fi."
],
"The system layout was set up using a advanced configuration that cannot be modified with the current version of this visual interface. This limitation will be removed in a future version of Agama.": [
- ""
+ "Tata letak sistem diatur menggunakan konfigurasi tingkat lanjut yang tidak dapat dimodifikasi dengan versi antarmuka visual saat ini. Keterbatasan ini akan dihilangkan pada versi Agama yang akan datang."
],
"The system will use %s as its default language.": [
"Sistem akan menggunakan %s sebagai bahasa default."
@@ -1338,6 +1371,9 @@ export default {
"Transactional root file system": [
"Sistem file root transaksional"
],
+ "Try again": [
+ "Coba lagi"
+ ],
"Type": [
"Jenis"
],
diff --git a/web/src/po/po.ja.js b/web/src/po/po.ja.js
index 552d3394ac..d296c17adf 100644
--- a/web/src/po/po.ja.js
+++ b/web/src/po/po.ja.js
@@ -561,6 +561,9 @@ export default {
"Hide details": [
"詳細を隠す"
],
+ "I have read and accept the [license] for %s": [
+ "%s に対する [ライセンス] を受け入れます"
+ ],
"IP Address": [
"IP アドレス"
],
@@ -690,6 +693,9 @@ export default {
"Language": [
"言語"
],
+ "License language": [
+ "ライセンスの言語"
+ ],
"Limits for the file system size. The final size will be a value between the given minimum and maximum. If no maximum is given then the file system will be as big as possible.": [
"ファイルシステムに対するサイズ制限を範囲指定します。最終的なサイズは最小値と最大値の間になります。最大値を指定しない場合、ファイルシステムはできる限り大きくなるように設定されます。"
],
@@ -699,6 +705,9 @@ export default {
"Loading installation environment, please wait.": [
"インストール環境を読み込んでいます。しばらくお待ちください。"
],
+ "Loading the installation repositories...": [
+ "インストール用リポジトリを読み込んでいます..."
+ ],
"Locale selection": [
"ロケールの選択"
],
@@ -1041,6 +1050,9 @@ export default {
"Remove min channel filter": [
"最小チャネルのフィルタを削除"
],
+ "Repository load failed": [
+ "リポジトリ読み込み失敗"
+ ],
"Reset location": [
"場所のリセット"
],
@@ -1173,6 +1185,9 @@ export default {
"Software selection": [
"ソフトウエア選択"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ "インストール用リポジトリのうち、読み込めなかったものがあります。これらのリポジトリ無しにはシステムをインストールできません。"
+ ],
"Something went wrong": [
"何らかの問題が発生しました"
],
@@ -1356,6 +1371,9 @@ export default {
"Transactional root file system": [
"トランザクション型のルートファイルシステム"
],
+ "Try again": [
+ "再試行"
+ ],
"Type": [
"種類"
],
diff --git a/web/src/po/po.nb_NO.js b/web/src/po/po.nb_NO.js
index c05c51ad40..072b3a1389 100644
--- a/web/src/po/po.nb_NO.js
+++ b/web/src/po/po.nb_NO.js
@@ -531,6 +531,9 @@ export default {
"Hide details": [
"Skjul detaljer"
],
+ "I have read and accept the [license] for %s": [
+ ""
+ ],
"IP Address": [
"IP Addresse"
],
@@ -978,6 +981,9 @@ export default {
"Remove min channel filter": [
"Fjern minimum kanal filter"
],
+ "Repository load failed": [
+ ""
+ ],
"Reset location": [
"Tilbakestill plassering"
],
@@ -1102,6 +1108,9 @@ export default {
"Software selection": [
"Valg av programvare"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ ""
+ ],
"Something went wrong": [
"Noe gikk galt"
],
@@ -1279,6 +1288,9 @@ export default {
"Transactional root file system": [
"Transaksjonell root filsystem"
],
+ "Try again": [
+ ""
+ ],
"Type": [
"Type"
],
diff --git a/web/src/po/po.pt_BR.js b/web/src/po/po.pt_BR.js
index bc3fde86b0..50b1721c45 100644
--- a/web/src/po/po.pt_BR.js
+++ b/web/src/po/po.pt_BR.js
@@ -561,6 +561,9 @@ export default {
"Hide details": [
"Ocultar Detalhes"
],
+ "I have read and accept the [license] for %s": [
+ ""
+ ],
"IP Address": [
"Endereço IP"
],
@@ -1032,6 +1035,9 @@ export default {
"Remove min channel filter": [
"Remover filtro de canal mínimo"
],
+ "Repository load failed": [
+ ""
+ ],
"Reset location": [
"Redefinir local"
],
@@ -1162,6 +1168,9 @@ export default {
"Software selection": [
"Seleção de software"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ ""
+ ],
"Something went wrong": [
"Alguma coisa deu errado"
],
diff --git a/web/src/po/po.ru.js b/web/src/po/po.ru.js
index c76e50dfcb..d39e4217b3 100644
--- a/web/src/po/po.ru.js
+++ b/web/src/po/po.ru.js
@@ -564,6 +564,9 @@ export default {
"Hide details": [
"Скрыть подробности"
],
+ "I have read and accept the [license] for %s": [
+ ""
+ ],
"IP Address": [
"IP-адрес"
],
@@ -1035,6 +1038,9 @@ export default {
"Remove min channel filter": [
"Удалить фильтр по минимальному каналу"
],
+ "Repository load failed": [
+ ""
+ ],
"Reset location": [
"Сбросить расположение"
],
@@ -1166,6 +1172,9 @@ export default {
"Software selection": [
"Выбор программного обеспечения"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ ""
+ ],
"Something went wrong": [
"Что-то пошло не так"
],
@@ -1349,6 +1358,9 @@ export default {
"Transactional root file system": [
"Транзакционная корневая файловая система"
],
+ "Try again": [
+ ""
+ ],
"Type": [
"Тип"
],
diff --git a/web/src/po/po.sv.js b/web/src/po/po.sv.js
index bc429fa3dc..0ca61adc50 100644
--- a/web/src/po/po.sv.js
+++ b/web/src/po/po.sv.js
@@ -564,6 +564,9 @@ export default {
"Hide details": [
"Dölj detaljer"
],
+ "I have read and accept the [license] for %s": [
+ "Jag har läst och accepterar [licensen] för %s"
+ ],
"IP Address": [
"IP address"
],
@@ -693,6 +696,9 @@ export default {
"Language": [
"Språk"
],
+ "License language": [
+ "Licens språk"
+ ],
"Limits for the file system size. The final size will be a value between the given minimum and maximum. If no maximum is given then the file system will be as big as possible.": [
"Gränser för filsystemets storlek. Den slutliga storleken kommer att vara ett värde mellan angivet minsta och maximal. Om inget maximalt anges kommer filsystemet att vara så stort som möjligt."
],
@@ -702,6 +708,9 @@ export default {
"Loading installation environment, please wait.": [
"Laddar installationsmiljö, vänligen vänta."
],
+ "Loading the installation repositories...": [
+ "Laddar installationsförråd..."
+ ],
"Locale selection": [
"Lokal val"
],
@@ -1044,6 +1053,9 @@ export default {
"Remove min channel filter": [
"Ta bort minimum kanal filter"
],
+ "Repository load failed": [
+ "Det gick inte att ladda förråd"
+ ],
"Reset location": [
"Återställ plats"
],
@@ -1177,6 +1189,9 @@ export default {
"Software selection": [
"Val av programvara"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ "Vissa installationsförråd kunde inte laddas. Utan dem kan systemet inte installeras."
+ ],
"Something went wrong": [
"Något gick fel"
],
@@ -1360,6 +1375,9 @@ export default {
"Transactional root file system": [
"Transaktionellt root filsystem"
],
+ "Try again": [
+ "Försök igen"
+ ],
"Type": [
"Typ"
],
diff --git a/web/src/po/po.tr.js b/web/src/po/po.tr.js
index 4781eb8562..8c157991a8 100644
--- a/web/src/po/po.tr.js
+++ b/web/src/po/po.tr.js
@@ -564,6 +564,9 @@ export default {
"Hide details": [
"Detayları gizle"
],
+ "I have read and accept the [license] for %s": [
+ ""
+ ],
"IP Address": [
"IP Adres"
],
@@ -1023,6 +1026,9 @@ export default {
"Remove min channel filter": [
"Min kanal filtresini kaldır"
],
+ "Repository load failed": [
+ ""
+ ],
"Reset location": [
"Konumu sıfırla"
],
@@ -1150,6 +1156,9 @@ export default {
"Software selection": [
"Yazılım seçimi"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ ""
+ ],
"Something went wrong": [
"Bir şeyler ters gitti"
],
@@ -1333,6 +1342,9 @@ export default {
"Transactional root file system": [
"İşlemsel kök dosya sistemi"
],
+ "Try again": [
+ ""
+ ],
"Type": [
"Tip"
],
diff --git a/web/src/po/po.zh_Hans.js b/web/src/po/po.zh_Hans.js
index 63d3205b03..d0779af007 100644
--- a/web/src/po/po.zh_Hans.js
+++ b/web/src/po/po.zh_Hans.js
@@ -525,6 +525,9 @@ export default {
"Hide details": [
"隐藏细节"
],
+ "I have read and accept the [license] for %s": [
+ ""
+ ],
"IP Address": [
"IP 地址"
],
@@ -969,6 +972,9 @@ export default {
"Remove min channel filter": [
"移除最小通道过滤器"
],
+ "Repository load failed": [
+ ""
+ ],
"Reset location": [
"重设位置"
],
@@ -1089,6 +1095,9 @@ export default {
"Software selection": [
"软件选择"
],
+ "Some installation repositories could not be loaded. The system cannot be installed without them.": [
+ ""
+ ],
"Something went wrong": [
"出了点问题"
],