Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package agama for openSUSE:Factory checked in at 2026-04-01 19:54:57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/agama (Old) and /work/SRC/openSUSE:Factory/.agama.new.21863 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "agama" Wed Apr 1 19:54:57 2026 rev:41 rq:1343948 version:0 Changes: -------- --- /work/SRC/openSUSE:Factory/agama/agama.changes 2026-03-18 16:49:54.643534669 +0100 +++ /work/SRC/openSUSE:Factory/.agama.new.21863/agama.changes 2026-04-01 19:55:31.105308474 +0200 @@ -1,0 +2,52 @@ +Tue Mar 24 13:01:38 UTC 2026 - Ladislav Slezák <[email protected]> + +- Include more details when saving the journal log +- Save separate libzypp.out.log file in the old y2log format + for libzypp debugging +- Store the complete journal log in JSON format to include hidden + fields and more details +- gh#agama-project/agama#3309 + +------------------------------------------------------------------- +Fri Mar 20 16:56:53 UTC 2026 - Imobach Gonzalez Sosa <[email protected]> + +- Enable agama-scripts service if needed (bsc#1259899). + +------------------------------------------------------------------- +Fri Mar 20 13:55:22 UTC 2026 - Imobach Gonzalez Sosa <[email protected]> + +- Enable SELinux if needed in unattended installations (bsc#1259890). + +------------------------------------------------------------------- +Fri Mar 20 08:46:19 UTC 2026 - Knut Anderssen <[email protected]> + +- Return error code when using "agama config generate", + "agama config load" or "agama config validate" does not validate + the given profile (bsc#1256951, gh#agama-project/agama#3304). + +------------------------------------------------------------------- +Fri Mar 20 07:54:46 UTC 2026 - Knut Anderssen <[email protected]> + +- Fix incorrect openAPI parameters (gh#agama-project/agama#3299). + - Do not define params for POST '/api/v2/action' + - Do not define params for PUT and PATCH '/api/v2/config + +------------------------------------------------------------------- +Thu Mar 19 21:11:29 UTC 2026 - Imobach Gonzalez Sosa <[email protected]> + +- Continue loading the configuration even if there is a network setup + error (gh#agama-project/agama#3306). + +------------------------------------------------------------------- +Wed Mar 18 22:29:10 UTC 2026 - Imobach Gonzalez Sosa <[email protected]> + +- Report problems when writing files and scripts (gh#agama-project/agama#3301). + +------------------------------------------------------------------- +Tue Mar 17 14:49:14 UTC 2026 - Michal Filka <[email protected]> + +- jsc#PED-15434 + - fixed autoyast_compat.json to cover earlier patch which + extended capabilities of sshPublicKey / sshPublicKeys + +------------------------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ agama.spec ++++++ --- /var/tmp/diff_new_pack.OZ1gw2/_old 2026-04-01 19:55:33.265397640 +0200 +++ /var/tmp/diff_new_pack.OZ1gw2/_new 2026-04-01 19:55:33.269397806 +0200 @@ -246,6 +246,8 @@ %license LICENSE %{_bindir}/agama-web-server %{_bindir}/agama-web-server.sh +%{_bindir}/agama-journal +%{_bindir}/agama-zypp-journal %{_bindir}/agama-proxy-setup %{_pam_vendordir}/agama %{_unitdir}/agama-web-server.service ++++++ agama.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/Cargo.lock new/agama/Cargo.lock --- old/agama/Cargo.lock 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/Cargo.lock 2026-03-24 16:36:47.000000000 +0100 @@ -71,6 +71,7 @@ "agama-software", "agama-utils", "async-trait", + "gettext-rs", "serde_json", "strum", "tempfile", @@ -342,7 +343,6 @@ name = "agama-software" version = "0.1.0" dependencies = [ - "agama-bootloader", "agama-l10n", "agama-security", "agama-utils", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-cli/src/config.rs new/agama/agama-cli/src/config.rs --- old/agama/agama-cli/src/config.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-cli/src/config.rs 2026-03-24 16:36:47.000000000 +0100 @@ -129,7 +129,7 @@ let valid = validate(&http_client, CliInput::Full(contents.clone()), false).await?; if !matches!(valid, ValidationOutcome::Valid) { - return Ok(()); + return Err(anyhow!("The profile is not valid")); } let model: api::Config = serde_json::from_str(&contents)?; @@ -138,12 +138,16 @@ monitor_progress(monitor).await?; } ConfigCommands::Validate { url_or_path, local } => { - let _ = if !local { + let validity = if !local { let http_client = build_http_client(api_url, opts.insecure, true).await?; - validate(&http_client, url_or_path, false).await + validate(&http_client, url_or_path, false).await? } else { - validate_local(url_or_path, opts.insecure) + validate_local(url_or_path, opts.insecure)? }; + + if !matches!(validity, ValidationOutcome::Valid) { + return Err(anyhow!("The profile is not valid")); + } } ConfigCommands::Generate { url_or_path } => { let http_client = build_http_client(api_url, opts.insecure, true).await?; @@ -293,7 +297,7 @@ println!("{}", &profile_json); let _ = validation_msg(&validity); - return Ok(()); + return Err(anyhow!("The profile is not valid")); } let config = api::Config::from_json(&profile_json, &context.source)?; @@ -303,10 +307,9 @@ let validity = validate(client, CliInput::Full(config_json.clone()), false).await?; if matches!(validity, ValidationOutcome::NotValid(_)) { - eprintln!( - "{} Internal error: the profile was made invalid by InstallSettings round trip", - style("\u{2717}").bold().red() - ); + return Err(anyhow!( + "Internal error: the profile became invalid during Config round trip" + )); } Ok(()) @@ -364,10 +367,14 @@ let status = command.status().context(format!("Running {:?}", command))?; // TODO: do nothing if the content of the file is unchanged if status.success() { - // FIXME: invalid profile still gets loaded let updated = std::fs::read_to_string(&path).context(format!("Reading from file {:?}", path))?; - validate(http_client, CliInput::Full(updated.clone()), false).await?; + let validity = validate(http_client, CliInput::Full(updated.clone()), false).await?; + + if !matches!(validity, ValidationOutcome::Valid) { + return Err(anyhow!("The profile is not valid")); + } + return Ok(serde_json::from_str(&updated)?); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-files/Cargo.toml new/agama/agama-files/Cargo.toml --- old/agama/agama-files/Cargo.toml 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-files/Cargo.toml 2026-03-24 16:36:47.000000000 +0100 @@ -8,6 +8,7 @@ agama-software = { version = "0.1.0", path = "../agama-software" } agama-utils = { path = "../agama-utils" } async-trait = "0.1.89" +gettext-rs = { version = "0.7.7", features = ["gettext-system"] } strum = "0.27.2" tempfile = "3.23.0" thiserror = "2.0.17" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-files/src/lib.rs new/agama/agama-files/src/lib.rs --- old/agama/agama-files/src/lib.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-files/src/lib.rs 2026-03-24 16:36:47.000000000 +0100 @@ -156,7 +156,7 @@ .await .unwrap(); - ctx.handler.call(message::WriteFiles).await.unwrap(); + ctx.handler.call(message::Finish).await.unwrap(); // Check that the file exists let expected_path = ctx.tmp_dir.path().join("etc/README.md"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-files/src/message.rs new/agama/agama-files/src/message.rs --- old/agama/agama-files/src/message.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-files/src/message.rs 2026-03-24 16:36:47.000000000 +0100 @@ -62,9 +62,9 @@ } #[derive(Clone)] -pub struct WriteFiles; +pub struct Finish; -impl Message for WriteFiles { +impl Message for Finish { type Reply = (); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-files/src/service.rs new/agama/agama-files/src/service.rs --- old/agama/agama-files/src/service.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-files/src/service.rs 2026-03-24 16:36:47.000000000 +0100 @@ -26,13 +26,19 @@ use agama_software::{self as software, Resolvable, ResolvableType}; use agama_utils::{ actor::{self, Actor, Handler, MessageHandler}, - api::files::{ - scripts::{self, ScriptsGroup, ScriptsRepository}, - user_file, ScriptsConfig, UserFile, + api::{ + files::{ + scripts::{self, ScriptsGroup, ScriptsRepository}, + user_file, Script, ScriptsConfig, UserFile, + }, + question::QuestionSpec, }, - progress, question, + command::enable_service, + progress, + question::{self, ask_question, AskError}, }; use async_trait::async_trait; +use gettextrs::gettext; use strum::IntoEnumIterator; use tokio::sync::Mutex; @@ -48,6 +54,8 @@ Software(#[from] software::service::Error), #[error(transparent)] Actor(#[from] actor::Error), + #[error(transparent)] + Questions(#[from] AskError), } const DEFAULT_SCRIPTS_DIR: &str = "run/agama/scripts"; @@ -142,29 +150,28 @@ } pub async fn add_scripts(&mut self, config: ScriptsConfig) -> Result<(), Error> { - let mut repo = self.scripts.lock().await; if let Some(scripts) = config.pre { for pre in scripts { - repo.add(pre.into())?; + self.add_script(pre.into()).await?; } } if let Some(scripts) = config.post_partitioning { for post in scripts { - repo.add(post.into())?; + self.add_script(post.into()).await?; } } if let Some(scripts) = config.post { for post in scripts { - repo.add(post.into())?; + self.add_script(post.into()).await?; } } let mut packages = vec![]; if let Some(scripts) = config.init { for init in scripts { - repo.add(init.into())?; + self.add_script(init.into()).await?; } packages.push(Resolvable::new("agama-scripts", ResolvableType::Package)); } @@ -176,6 +183,59 @@ .await?; Ok(()) } + + async fn add_script(&self, script: Script) -> Result<(), Error> { + let result = { + let mut repo = self.scripts.lock().await; + repo.add(script.clone()) + }; + + let mut attempt = 1; + while let Err(error) = &result { + tracing::error!("Failed to write the script {}: {error}.", script.name()); + + // TRANSLATORS: %s is replaced by the script name. + let text = &gettext("Failed to retrieve the script %s. Do you want to try again?") + .replace("%s", script.name()); + let question = QuestionSpec::new(text, "write_script_failed") + .with_yes_no_actions() + .with_data(&[ + ("attempt", &attempt.to_string()), + ("details", &error.to_string()), + ]); + let answer = ask_question(&self.questions, question).await?; + if answer.action == "No" { + return Ok(()); + } + attempt += 1; + } + + Ok(()) + } + + async fn write_file(&self, file: &UserFile) -> Result<(), Error> { + let mut attempt = 1; + while let Err(error) = file.write(&self.install_dir).await { + tracing::error!("Failed to write the file {}: {error}.", file.destination); + + // TRANSLATORS: %s is replaced by the script name. + let text = &gettext("Failed to write the file %s. Do you want to try again?") + .replace("%s", &file.destination); + let question = QuestionSpec::new(text, "write_file_failed") + .with_yes_no_actions() + .with_data(&[ + ("attempt", &attempt.to_string()), + ("details", &error.to_string()), + ]); + let answer = ask_question(&self.questions, question).await?; + if answer.action == "No" { + return Ok(()); + } + attempt += 1; + } + + Ok(()) + } } impl Actor for Service { @@ -234,13 +294,17 @@ } #[async_trait] -impl MessageHandler<message::WriteFiles> for Service { - async fn handle(&mut self, _message: message::WriteFiles) -> Result<(), Error> { +impl MessageHandler<message::Finish> for Service { + async fn handle(&mut self, _message: message::Finish) -> Result<(), Error> { for file in &self.files { - if let Err(error) = file.write(&self.install_dir).await { - tracing::error!("Failed to write file {}: {error}", file.destination); - } + self.write_file(file).await?; } + + let scripts = self.scripts.lock().await; + if !scripts.by_group(ScriptsGroup::Init).is_empty() { + enable_service(&self.install_dir, "agama-scripts"); + } + Ok(()) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-lib/src/logs.rs new/agama/agama-lib/src/logs.rs --- old/agama/agama-lib/src/logs.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-lib/src/logs.rs 2026-03-24 16:36:47.000000000 +0100 @@ -24,7 +24,6 @@ use std::fs; use std::fs::File; use std::io; -use std::io::Write; use std::os::unix::fs::PermissionsExt; use std::path::{Path, PathBuf}; use std::process::Command; @@ -32,9 +31,11 @@ use utoipa::ToSchema; use zypp_agama::SOLVER_TESTCASE_DIR; -const DEFAULT_COMMANDS: [(&str, &str); 2] = [ +const DEFAULT_COMMANDS: [(&str, &str); 4] = [ // (<command to be executed>, <file name used for storing result of the command>) - ("journalctl", "journald"), + ("journalctl -o json", "journal_json"), + ("agama-journal", "journal"), + ("agama-zypp-journal", "libzypp"), ("rpm -qa", "rpm-qa"), ]; @@ -178,19 +179,34 @@ fn store(&self) -> Result<(), io::Error> { let cmd_parts = self.cmd.split_whitespace().collect::<Vec<&str>>(); let file_path = self.to(); - let output = Command::new(cmd_parts[0]) - .args(cmd_parts[1..].iter()) - .output()?; - if !output.stdout.is_empty() { - let mut file_stdout = File::create(format!("{}.out.log", file_path.display()))?; + let mut stdout_name = file_path.clone(); + stdout_name.set_extension("out.log"); - file_stdout.write_all(&output.stdout)?; - } - if !output.stderr.is_empty() { - let mut file_stderr = File::create(format!("{}.err.log", file_path.display()))?; + let mut stderr_name = file_path.clone(); + stderr_name.set_extension("err.log"); + + let file_stdout = File::create(&stdout_name)?; + let file_stderr = File::create(&stderr_name)?; - file_stderr.write_all(&output.stderr)?; + let status = Command::new(cmd_parts[0]) + .args(&cmd_parts[1..]) + .stdout(file_stdout) + .stderr(file_stderr) + .status()?; + + tracing::info!( + "Command {} finished with status: {}", + self.cmd, + status.code().unwrap_or_default() + ); + + // delete the created files if they are empty + if fs::metadata(&stdout_name)?.len() == 0 { + let _ = fs::remove_file(&stdout_name); + } + if fs::metadata(&stderr_name)?.len() == 0 { + let _ = fs::remove_file(&stderr_name); } Ok(()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-manager/src/actions.rs new/agama/agama-manager/src/actions.rs --- old/agama/agama-manager/src/actions.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-manager/src/actions.rs 2026-03-24 16:36:47.000000000 +0100 @@ -113,7 +113,7 @@ .await?; self.l10n.call(l10n::message::Install).await?; self.software.call(software::message::Finish).await?; - self.files.call(files::message::WriteFiles).await?; + self.files.call(files::message::Finish).await?; self.network.install().await?; self.proxy.call(proxy::message::Finish).await?; self.hostname.call(hostname::message::Install).await?; @@ -294,12 +294,9 @@ .await?; } - if let Some(network) = config.network.clone() { - self.progress - .call(progress::message::Next::new(Scope::Manager)) - .await?; - self.network.update_config(network).await?; - self.network.apply().await?; + // FIXME: report the error in a proper way. + if let Err(error) = self.set_network(&config).await { + tracing::error!("Failed to set up the network: {error}"); } match &product { @@ -307,6 +304,18 @@ self.progress .call(progress::message::Next::new(Scope::Manager)) .await?; + self.software + .call(software::message::SetConfig::new( + Arc::clone(product), + config.software.clone(), + )) + .await?; + + self.set_selinux().await?; + + self.progress + .call(progress::message::Next::new(Scope::Manager)) + .await?; let future = self .storage .call(storage::message::SetConfig::new( @@ -325,16 +334,6 @@ config.bootloader.clone(), )) .await?; - - self.progress - .call(progress::message::Next::new(Scope::Manager)) - .await?; - self.software - .call(software::message::SetConfig::new( - Arc::clone(product), - config.software.clone(), - )) - .await?; } None => { @@ -344,6 +343,52 @@ } Ok(()) + } + + async fn set_network(&self, config: &Config) -> Result<(), service::Error> { + let Some(network) = config.network.clone() else { + return Ok(()); + }; + + self.progress + .call(progress::message::Next::new(Scope::Manager)) + .await?; + self.network.update_config(network).await?; + self.network.apply().await?; + + Ok(()) + } + + // Enables/Disables SELinux in the installed system. + // + // If the "selinux" pattern is selected, set the "security=selinux" boot + // kernel parameter. + // + // NOTE: this logic should live in another place, like "agama-security". + // It is temporarily here to fix bsc#1259890. + async fn set_selinux(&self) -> Result<(), service::Error> { + let selinux_selected = self + .software + .call(software::message::IsPatternSelected::new( + "selinux".to_string(), + )) + .await?; + + let value = if selinux_selected { + "security=selinux" + } else { + "security=" + }; + let message = agama_bootloader::message::SetKernelArg { + id: "selinux".to_string(), + value: value.to_string(), + }; + + if let Err(error) = self.bootloader.cast(message) { + tracing::warn!("Failed to send to bootloader new selinux state: {error:?}"); + } + + Ok(()) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-manager/src/service.rs new/agama/agama-manager/src/service.rs --- old/agama/agama-manager/src/service.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-manager/src/service.rs 2026-03-24 16:36:47.000000000 +0100 @@ -281,7 +281,6 @@ progress.clone(), self.questions.clone(), security.clone(), - bootloader.clone(), ) .start() .await? @@ -730,7 +729,6 @@ /// Sets the user configuration with the given values. async fn handle(&mut self, message: message::SetConfig) -> Result<(), Error> { checks::check_stage(&self.progress, Stage::Configuring).await?; - tracing::debug!("DEBUG: SetConfig handler (calling set_config)"); self.set_config(message.config).await } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-proxy/src/service.rs new/agama/agama-proxy/src/service.rs --- old/agama/agama-proxy/src/service.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-proxy/src/service.rs 2026-03-24 16:36:47.000000000 +0100 @@ -19,10 +19,7 @@ // find current contact information at www.suse.com. use crate::model; -use std::{ - path::{Path, PathBuf}, - process, -}; +use std::path::{Path, PathBuf}; use agama_utils::{ actor::{self, Actor, Handler, MessageHandler}, @@ -30,6 +27,7 @@ self, event::{self}, }, + command::enable_service, }; use async_trait::async_trait; @@ -203,31 +201,9 @@ pub fn config_path(&self) -> PathBuf { self.install_dir.join(PROXY_PATH) } - pub fn enable_services(&self) -> Result<(), Error> { - self.enable_service("setup-systemd-proxy-env.service")?; - self.enable_service("setup-systemd-proxy-env.path")?; - Ok(()) - } - - pub fn enable_service(&self, name: &str) -> Result<(), Error> { - let mut command = process::Command::new("chroot"); - let path = self.install_dir.to_str().unwrap(); - command.args([path, "systemctl", "enable", name]); - - match command.output() { - Ok(output) => { - if !output.status.success() { - tracing::error!("Failed to enable the {name} service: {output:?}") - } - } - Err(error) => { - tracing::error!( - "Failed to run the command to enable the {name} service command: {error}" - ); - } - } - - Ok(()) + pub fn enable_services(&self) { + enable_service(&self.install_dir, "setup-systemd-proxy-env.service"); + enable_service(&self.install_dir, "setup-systemd-proxy-env.path"); } } @@ -277,7 +253,7 @@ std::fs::create_dir_all(parent)?; } config.write_to(&path)?; - self.enable_services()?; + self.enable_services(); } Ok(()) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-server/src/server/web.rs new/agama/agama-server/src/server/web.rs --- old/agama/agama-server/src/server/web.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-server/src/server/web.rs 2026-03-24 16:36:47.000000000 +0100 @@ -210,12 +210,10 @@ put, path = "/config", context_path = "/api/v2", + request_body(content = Value, description = "Configuration to apply."), responses( (status = 200, description = "The configuration was replaced. Other operations can be running in background."), (status = 400, description = "Not possible to replace the configuration.") - ), - params( - ("config" = Value, description = "Configuration to apply.") ) )] async fn put_config(State(state): State<ServerState>, Json(json): Json<Value>) -> ServerResult<()> { @@ -232,12 +230,10 @@ patch, path = "/config", context_path = "/api/v2", + request_body(content = Patch, description = "Changes in the configuration."), responses( (status = 200, description = "The configuration was patched. Other operations can be running in background."), (status = 400, description = "Not possible to patch the configuration.") - ), - params( - ("patch" = Patch, description = "Changes in the configuration.") ) )] async fn patch_config( @@ -410,13 +406,11 @@ post, path = "/action", context_path = "/api/v2", + request_body(content = Action, description = "Description of the action to run."), responses( (status = 200, description = "Action successfully ran."), (status = 400, description = "Not possible to run the action.", body = Object), (status = 422, description = "Action blocked by backend state", body = Object) - ), - params( - ("action" = Action, description = "Description of the action to run."), ) )] async fn run_action( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-software/Cargo.toml new/agama/agama-software/Cargo.toml --- old/agama/agama-software/Cargo.toml 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-software/Cargo.toml 2026-03-24 16:36:47.000000000 +0100 @@ -5,7 +5,6 @@ edition.workspace = true [dependencies] -agama-bootloader = { path = "../agama-bootloader" } agama-l10n = { path = "../agama-l10n" } agama-utils = { path = "../agama-utils" } agama-security = { path = "../agama-security" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-software/src/message.rs new/agama/agama-software/src/message.rs --- old/agama/agama-software/src/message.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-software/src/message.rs 2026-03-24 16:36:47.000000000 +0100 @@ -133,3 +133,18 @@ impl Message for SetLocale { type Reply = (); } + +#[derive(Clone)] +pub struct IsPatternSelected { + pub name: String, +} + +impl Message for IsPatternSelected { + type Reply = bool; +} + +impl IsPatternSelected { + pub fn new(name: String) -> Self { + Self { name } + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-software/src/service.rs new/agama/agama-software/src/service.rs --- old/agama/agama-software/src/service.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-software/src/service.rs 2026-03-24 16:36:47.000000000 +0100 @@ -26,7 +26,6 @@ zypp_server::{self, SoftwareAction, ZyppServer}, Model, ResolvableType, }; -use agama_bootloader; use agama_security as security; use agama_utils::{ actor::{self, Actor, Handler, MessageHandler}, @@ -92,7 +91,6 @@ progress: Handler<progress::Service>, questions: Handler<question::Service>, security: Handler<security::Service>, - bootloader: Handler<agama_bootloader::Service>, } impl Starter { @@ -102,7 +100,6 @@ progress: Handler<progress::Service>, questions: Handler<question::Service>, security: Handler<security::Service>, - bootloader: Handler<agama_bootloader::Service>, ) -> Self { Self { model: None, @@ -111,7 +108,6 @@ progress, questions, security, - bootloader, } } @@ -158,7 +154,6 @@ progress: self.progress, product: None, kernel_cmdline, - bootloader: self.bootloader, }; service.setup().await?; Ok(actor::spawn(service)) @@ -181,7 +176,6 @@ product: Option<Arc<RwLock<ProductSpec>>>, selection: SoftwareSelection, kernel_cmdline: KernelCmdline, - bootloader: Handler<agama_bootloader::Service>, } #[derive(Default)] @@ -199,9 +193,8 @@ progress: Handler<progress::Service>, questions: Handler<question::Service>, security: Handler<security::Service>, - bootloader: Handler<agama_bootloader::Service>, ) -> Starter { - Starter::new(events, issues, progress, questions, security, bootloader) + Starter::new(events, issues, progress, questions, security) } pub async fn setup(&mut self) -> Result<(), Error> { @@ -217,26 +210,6 @@ Ok(()) } - fn update_selinux(&self, state: &SoftwareState) { - let selinux_selected = state.resolvables.to_vec().iter().any(|(name, typ, state)| { - typ == &ResolvableType::Pattern && name == "selinux" && state.selected() - }); - - let value = if selinux_selected { - "security=selinux" - } else { - "security=" - }; - let message = agama_bootloader::message::SetKernelArg { - id: "selinux".to_string(), - value: value.to_string(), - }; - let res = self.bootloader.cast(message); - if res.is_err() { - tracing::warn!("Failed to send to bootloader new selinux state: {:?}", res); - } - } - /// Updates the proposal and the service state. /// /// This function performs the following actions: @@ -258,8 +231,6 @@ SoftwareState::build_from(&product, &state.config, &state.system, &self.selection) }; - self.update_selinux(&new_state); - tracing::info!("Wanted software state: {new_state:?}"); { let mut state = self.state.write().await; @@ -513,6 +484,26 @@ } } +#[async_trait] +impl MessageHandler<message::IsPatternSelected> for Service { + async fn handle(&mut self, message: message::IsPatternSelected) -> Result<bool, Error> { + let state = self.state.read().await; + let Some(software_state) = &state.state else { + return Ok(false); + }; + + let selected = software_state + .resolvables + .to_vec() + .iter() + .any(|(name, typ, state)| { + typ == &ResolvableType::Pattern && name == &message.name && state.selected() + }); + + Ok(selected) + } +} + const LIVE_REPO_DIR: &str = "run/initramfs/live/install"; const DUD_REPO_DIR: &str = "var/lib/agama/dud/repo"; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-software/src/test_utils.rs new/agama/agama-software/src/test_utils.rs --- old/agama/agama-software/src/test_utils.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-software/src/test_utils.rs 2026-03-24 16:36:47.000000000 +0100 @@ -27,7 +27,7 @@ }, issue, products::ProductSpec, - progress, question, test, + progress, question, }; use async_trait::async_trait; @@ -90,9 +90,7 @@ questions: Handler<question::Service>, ) -> Handler<Service> { let security = start_security_service(questions.clone()).await; - let dbus = test::dbus::connection().await.unwrap(); - let bootloader = agama_bootloader::test_utils::start_service(issues.clone(), dbus).await; - Service::starter(events, issues, progress, questions, security, bootloader) + Service::starter(events, issues, progress, questions, security) .with_model(TestModel {}) .start() .await diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/agama-utils/src/command.rs new/agama/agama-utils/src/command.rs --- old/agama/agama-utils/src/command.rs 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/agama-utils/src/command.rs 2026-03-24 16:36:47.000000000 +0100 @@ -77,3 +77,26 @@ .open(path)?; Ok(file) } + +/// Convenience function to enable a service +pub fn enable_service<P: AsRef<Path>>(root_dir: P, name: &str) { + let mut command = std::process::Command::new("chroot"); + command + .arg(root_dir.as_ref()) + .args(["systemctl", "enable", name]); + + match command.output() { + Ok(output) => { + if output.status.success() { + tracing::info!("Enabled the {name} service"); + } else { + tracing::error!("Failed to enable the {name} service: {output:?}") + } + } + Err(error) => { + tracing::error!( + "Failed to run the command to enable the {name} service command: {error}" + ); + } + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/install.sh new/agama/install.sh --- old/agama/install.sh 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/install.sh 2026-03-24 16:36:47.000000000 +0100 @@ -39,6 +39,8 @@ install -D -t "${DESTDIR}${bindir}" "${SRCDIR}/target/${RUST_TARGET}/agama-proxy-setup" install -D -t "${DESTDIR}${bindir}" "${SRCDIR}/target/${RUST_TARGET}/agama-web-server" install -D -t "${DESTDIR}${bindir}" "${SRCDIR}/share/agama-web-server.sh" +install -D -t "${DESTDIR}${bindir}" "${SRCDIR}/share/agama-journal" +install -D -t "${DESTDIR}${bindir}" "${SRCDIR}/share/agama-zypp-journal" install6 -D -p "${SRCDIR}"/share/agama.pam "${DESTDIR}${pamvendordir}"/agama diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/package/agama.changes new/agama/package/agama.changes --- old/agama/package/agama.changes 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/package/agama.changes 2026-03-24 16:36:47.000000000 +0100 @@ -1,4 +1,56 @@ ------------------------------------------------------------------- +Tue Mar 24 13:01:38 UTC 2026 - Ladislav Slezák <[email protected]> + +- Include more details when saving the journal log +- Save separate libzypp.out.log file in the old y2log format + for libzypp debugging +- Store the complete journal log in JSON format to include hidden + fields and more details +- gh#agama-project/agama#3309 + +------------------------------------------------------------------- +Fri Mar 20 16:56:53 UTC 2026 - Imobach Gonzalez Sosa <[email protected]> + +- Enable agama-scripts service if needed (bsc#1259899). + +------------------------------------------------------------------- +Fri Mar 20 13:55:22 UTC 2026 - Imobach Gonzalez Sosa <[email protected]> + +- Enable SELinux if needed in unattended installations (bsc#1259890). + +------------------------------------------------------------------- +Fri Mar 20 08:46:19 UTC 2026 - Knut Anderssen <[email protected]> + +- Return error code when using "agama config generate", + "agama config load" or "agama config validate" does not validate + the given profile (bsc#1256951, gh#agama-project/agama#3304). + +------------------------------------------------------------------- +Fri Mar 20 07:54:46 UTC 2026 - Knut Anderssen <[email protected]> + +- Fix incorrect openAPI parameters (gh#agama-project/agama#3299). + - Do not define params for POST '/api/v2/action' + - Do not define params for PUT and PATCH '/api/v2/config + +------------------------------------------------------------------- +Thu Mar 19 21:11:29 UTC 2026 - Imobach Gonzalez Sosa <[email protected]> + +- Continue loading the configuration even if there is a network setup + error (gh#agama-project/agama#3306). + +------------------------------------------------------------------- +Wed Mar 18 22:29:10 UTC 2026 - Imobach Gonzalez Sosa <[email protected]> + +- Report problems when writing files and scripts (gh#agama-project/agama#3301). + +------------------------------------------------------------------- +Tue Mar 17 14:49:14 UTC 2026 - Michal Filka <[email protected]> + +- jsc#PED-15434 + - fixed autoyast_compat.json to cover earlier patch which + extended capabilities of sshPublicKey / sshPublicKeys + +------------------------------------------------------------------- Tue Mar 17 12:15:22 UTC 2026 - Imobach Gonzalez Sosa <[email protected]> - Version 19 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/package/agama.spec new/agama/package/agama.spec --- old/agama/package/agama.spec 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/package/agama.spec 2026-03-24 16:36:47.000000000 +0100 @@ -246,6 +246,8 @@ %license LICENSE %{_bindir}/agama-web-server %{_bindir}/agama-web-server.sh +%{_bindir}/agama-journal +%{_bindir}/agama-zypp-journal %{_bindir}/agama-proxy-setup %{_pam_vendordir}/agama %{_unitdir}/agama-web-server.service diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/share/agama-journal new/agama/share/agama-journal --- old/agama/share/agama-journal 1970-01-01 01:00:00.000000000 +0100 +++ new/agama/share/agama-journal 2026-03-24 16:36:47.000000000 +0100 @@ -0,0 +1,131 @@ +#!/bin/bash +# +# Copyright (c) [2026] 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. + +# Helper script which prints the messages from the systemd journal with +# additional details like code location. + +# All script arguments are passed to journalctl, you can use it to filter the +# displayed messages, e.g. filter by unit with '-u sshd.service', filter by time +# with '--since "5 minutes ago"' or show only errors and higher priority with +# '-p err'.. + +# The timestamps are printed in the local time, to print the UTC time +# run with TZ=UTC. Hint: You can set any time zone, e.g. TZ=America/New_York. + +# It accepts special "--input-file <file>" parameter to load the data from +# previously saved journal in JSON format. + + +# Use colors if the output goes to a terminal +if [ -t 1 ]; then + USE_COLORS="true" +else + USE_COLORS="false" +fi + +SHOW_MICROSECONDS="false" +INPUT_FILE="" +JOURNAL_ARGS=() + +while [ $# -gt 0 ]; do + case "$1" in + # Allow using a JSON file as input + --input-file) + INPUT_FILE="$2" + shift 2 + ;; + --microseconds) + SHOW_MICROSECONDS="true" + shift + ;; + *) + JOURNAL_ARGS+=("$1") + shift + ;; + esac +done + +process_logs() { + if [ -n "$INPUT_FILE" ]; then + cat "$INPUT_FILE" + else + journalctl -o json "$@" + fi | jq --unbuffered --arg use_colors "$USE_COLORS" --arg show_microseconds "$SHOW_MICROSECONDS" -r ' + # Helper function to get color based on priority + def get_color: + { + "0": "\u001b[1;91m", # Emergency - Bold Bright Red + "1": "\u001b[1;91m", # Alert - Bold Bright Red + "2": "\u001b[1;91m", # Critical - Bold Bright Red + "3": "\u001b[1;31m", # Error - Bold Red + "4": "\u001b[93m", # Warning - Bright Yellow + "5": "\u001b[1m", # Notice - Bold white + "6": "", # Informational - Default + "7": "\u001b[90m" # Debug - Grey + }[tostring] // ""; + + # Helper function to format timestamp + def format_timestamp: + # The journal time is in microseconds + (tonumber / 1000000 | strflocaltime("%b %e %H:%M:%S")) + + (if $show_microseconds == "true" then "." + (tostring | .[-6:]) else "" end); + + # Helper function to build the source code location string + def format_location: + [ + (.CODE_FILE | select(. != null and . != "") | tostring), + (.CODE_FUNC | select(. != null and . != "") | "(" + tostring + ")"), + (.CODE_LINE | select(. != null and . != "") | ":" + tostring + ":") + ] + | join("") + | select(. != ""); + + # Helper function to combine syslog identifier and PID + def format_syslog_pid: + [ + (.SYSLOG_IDENTIFIER | select(. != null and . != "")), + (._PID | select(. != null and . != "") | "[" + tostring + "]:") + ] + | join("") + | select(. != ""); + + # Join the requested fields into a single line + (.PRIORITY | get_color) as $color | + [ + (.__REALTIME_TIMESTAMP | select(. != null and . != "") | format_timestamp), + (.PRIORITY | select(. != null and . != "") | "<" + tostring + ">"), + format_syslog_pid, + format_location, + (.ZYPP_GROUP | select(. != null and . != "") | "[" + tostring + "]"), + (.MESSAGE | select(. != null and . != "") | tostring | if $use_colors == "true" and $color != "" then $color + . + "\u001b[0m" else . end) + ] + # Remove any missing/empty values and join them with a space + | map(select(. != null and . != "")) + | join(" ") +' +} + +if [ "$USE_COLORS" = "true" ] && [ -n "$PAGER" ]; then + process_logs "${JOURNAL_ARGS[@]}" | $PAGER +else + process_logs "${JOURNAL_ARGS[@]}" +fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/share/agama-zypp-journal new/agama/share/agama-zypp-journal --- old/agama/share/agama-zypp-journal 1970-01-01 01:00:00.000000000 +0100 +++ new/agama/share/agama-zypp-journal 2026-03-24 16:36:47.000000000 +0100 @@ -0,0 +1,119 @@ +#!/bin/bash +# +# Copyright (c) [2026] 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. + +# Helper script which extracts the libzypp messages from the systemd journal +# and prints them in the YaST y2log compatible format. + +# All script arguments are passed to journalctl, you can use it to filter the +# displayed messages, e.g. by date with '--since "5 minutes ago"' or show only +# errors and higher priority with '-p err'. + +# It accepts special "--input-file <file>" parameter to load the data from +# previously saved journal in JSON format. + + +# Use colors if the output goes to a terminal +if [ -t 1 ]; then + USE_COLORS="true" +else + USE_COLORS="false" +fi + +INPUT_FILE="" +JOURNAL_ARGS=() + +while [ $# -gt 0 ]; do + case "$1" in + # Allow using a JSON file as input + --input-file) + INPUT_FILE="$2" + shift 2 + ;; + *) + JOURNAL_ARGS+=("$1") + shift + ;; + esac +done + +process_logs() { + if [ -n "$INPUT_FILE" ]; then + cat "$INPUT_FILE" + else + journalctl -u agama-web-server.service -o json "$@" + fi | jq --unbuffered --arg use_colors "$USE_COLORS" -r ' + # Helper function to get color based on priority + def get_color: + { + "5": "\u001b[1;91m", # Internal error - Bold Bright Red + "4": "\u001b[1;95m", # Security error - Bold Bright Magenta + "3": "\u001b[1;31m", # Error - Bold Red + "2": "\u001b[93m", # Warning - Bright Yellow + "1": "", # Milestone - Default + "0": "\u001b[90m" # Debug - Grey + }[tostring] // "\u001b[35m"; # Missing or unknown - Magenta + + # Helper function to format timestamp (uses UTC) + def format_timestamp: + # The journal time is in microseconds + (tonumber / 1000000 | strflocaltime("%Y-%m-%d %H:%M:%S")); + + # Helper function to build the source code location string + def format_location: + [ + (.CODE_FILE | select(. != null and . != "") | tostring), + (.CODE_FUNC | select(. != null and . != "") | "(" + tostring + ")"), + (.CODE_LINE | select(. != null and . != "") | ":" + tostring) + ] + | join(""); + + # Helper function to combine hostname and PID + def format_syslog_pid: + [ + ._HOSTNAME, + (._PID | select(. != null and . != "") | "(" + tostring + ")") + ] + | join("") + | select(. != ""); + + select(.COMPONENT == "libzypp" or .COMPONENT == "zypp-agama-sys") | + # Join the requested fields into a single line + (.ZYPP_LEVEL | get_color) as $color | + [ + (.__REALTIME_TIMESTAMP | select(. != null and . != "") | format_timestamp), + (((.ZYPP_LEVEL | select(. != null and . != "") | tostring) // "1") | "<" + tostring + ">"), + format_syslog_pid, + "[" + ((.ZYPP_GROUP | select(. != null and . != "") | tostring) // .COMPONENT) + "]", + format_location, + (.MESSAGE | select(. != null and . != "") | tostring | if $use_colors == "true" and $color != "" then $color + . + "\u001b[0m" else . end) + ] + # Remove any missing/empty values and join them with a space + | map(select(. != null and . != "")) + | join(" ") +' +} + +if [ "$USE_COLORS" = "true" ] && [ -n "$PAGER" ]; then + process_logs "${JOURNAL_ARGS[@]}" | $PAGER +else + process_logs "${JOURNAL_ARGS[@]}" +fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/agama/zypp-agama/zypp-agama-sys/c-layer/lib.cxx new/agama/zypp-agama/zypp-agama-sys/c-layer/lib.cxx --- old/agama/zypp-agama/zypp-agama-sys/c-layer/lib.cxx 2026-03-17 15:08:44.000000000 +0100 +++ new/agama/zypp-agama/zypp-agama-sys/c-layer/lib.cxx 2026-03-24 16:36:47.000000000 +0100 @@ -33,6 +33,30 @@ #include <systemd/sd-journal.h> +// helper macro for logging the code location from where libzypp is called +#define LOG_LOCATION(message) \ + do { \ + std::string line("CODE_LINE="); \ + line.append(std::to_string(__LINE__)); \ + sd_journal_send_with_location("CODE_FILE=" __FILE__, line.c_str(), \ + __func__, "PRIORITY=%i", LOG_NOTICE, \ + "MESSAGE=%s", (message), \ + "COMPONENT=zypp-agama-sys", NULL); \ + } while (0) + +// helper function to hide the password in URL +std::string filter_url(const char *url) { + std::string result(url); + try { + zypp::Url z_url(url); + // asString() by default does not include the password + result = z_url.asString(); + } catch (const zypp::Exception &excpt) { + // if the URL is invalid, just return the original string + } + return result; +} + struct Zypp { zypp::ZYpp::Ptr zypp_pointer; zypp::RepoManager *repo_manager; @@ -100,7 +124,7 @@ // see "man sd_journal_send_with_location" sd_journal_send_with_location( file.c_str(), line.c_str(), zypp_func, "PRIORITY=%i", level, - "MESSAGE=[%s] %s", zypp_group.c_str(), zypp_message.c_str(), + "MESSAGE=%s", zypp_message.c_str(), // some custom data to allow easy filtering of the libzypp messages "COMPONENT=libzypp", "ZYPP_GROUP=%s", zypp_group.c_str(), "ZYPP_LEVEL=%i", zypp_level, NULL); @@ -119,6 +143,7 @@ }; void free_zypp(struct Zypp *zypp) noexcept { + LOG_LOCATION("Dropping libzypp"); // ensure that target is unloaded otherwise nasty things can happen if new // zypp is created in different thread zypp->zypp_pointer->getTarget()->unload(); @@ -129,6 +154,7 @@ } static zypp::ZYpp::Ptr zypp_ptr() { + LOG_LOCATION("Initializing libzypp"); sd_journal_print(LOG_NOTICE, "Redirecting libzypp logs to systemd journal"); // log to systemd journal using our specific formatter @@ -146,6 +172,10 @@ void switch_target(struct Zypp *zypp, const char *root, struct Status *status) noexcept { + std::string message("Switching target to: "); + message.append(root); + LOG_LOCATION(message.c_str()); + const std::string root_str(root); try { zypp->zypp_pointer->initializeTarget(root_str, @@ -179,6 +209,8 @@ struct DownloadResolvableCallbacks *download_callbacks, struct SecurityCallbacks *security_callbacks, struct InstallCallbacks *install_callbacks) noexcept { + LOG_LOCATION("Starting package installation"); + try { set_zypp_resolvable_download_callbacks(download_callbacks); set_zypp_security_callbacks(security_callbacks); @@ -205,6 +237,10 @@ // target and merge it in rust struct Zypp *init_target(const char *root, struct Status *status, ProgressCallback progress, void *user_data) noexcept { + std::string message("Initializing target: "); + message.append(root); + LOG_LOCATION(message.c_str()); + if (the_zypp.zypp_pointer != NULL) { STATUS_ERROR(status, "Cannot have two init_target concurrently, " "libzypp not ready for this. Call free_zypp first."); @@ -314,6 +350,10 @@ void resolvable_select(struct Zypp *_zypp, const char *name, enum RESOLVABLE_KIND kind, enum RESOLVABLE_SELECTED who, struct Status *status) noexcept { + std::string message("Selecting resolvable: "); + message.append(name); + LOG_LOCATION(message.c_str()); + if (who == RESOLVABLE_SELECTED::NOT_SELECTED) { STATUS_OK(status); return; @@ -336,6 +376,10 @@ enum RESOLVABLE_KIND kind, enum RESOLVABLE_SELECTED who, struct Status *status) noexcept { + std::string message("Unselecting resolvable: "); + message.append(name); + LOG_LOCATION(message.c_str()); + STATUS_OK(status); if (who == RESOLVABLE_SELECTED::NOT_SELECTED) { return; @@ -354,7 +398,8 @@ } void resolvable_reset_all(struct Zypp *_zypp) noexcept { - MIL << "Resetting status of all resolvables" << std::endl; + LOG_LOCATION("Resetting status of all resolvables"); + for (auto &item : zypp::ResPool::instance()) item.statusReset(); } @@ -384,6 +429,8 @@ struct Patterns get_patterns(struct Zypp *zypp, struct Status *status) noexcept { + LOG_LOCATION("Getting patterns"); + auto iterator = zypp->zypp_pointer->pool().proxy().byKind(zypp::ResKind::pattern); @@ -426,6 +473,7 @@ struct Products get_products(struct Zypp *zypp, struct Status *status) noexcept { + LOG_LOCATION("Getting products"); auto iterator = zypp->zypp_pointer->pool().proxy().byKind(zypp::ResKind::product); @@ -460,6 +508,7 @@ bool run_solver(struct Zypp *zypp, bool only_required, struct Status *status) noexcept { + LOG_LOCATION("Running solver"); try { STATUS_OK(status); auto resolver = zypp->zypp_pointer->resolver(); @@ -482,6 +531,13 @@ void add_service(struct Zypp *zypp, const char *alias, const char *url, struct Status *status) noexcept { + std::string message("Adding service: "); + message.append(alias); + message.append(" ("); + message.append(filter_url(url)); + message.append(")"); + LOG_LOCATION(message.c_str()); + if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; @@ -498,11 +554,19 @@ } bool create_solver_testcase(struct Zypp *zypp, const char *dir) noexcept { + std::string message("Creating solver testcase in directory "); + message.append(dir); + LOG_LOCATION(message.c_str()); + return zypp->zypp_pointer->resolver()->createSolverTestcase(dir); } void refresh_service(struct Zypp *zypp, const char *alias, struct Status *status) noexcept { + std::string message("Refreshing service: "); + message.append(alias); + LOG_LOCATION(message.c_str()); + if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; @@ -526,6 +590,10 @@ struct Status *status, struct DownloadProgressCallbacks *callbacks, struct SecurityCallbacks *security_callbacks) noexcept { + std::string message("Refreshing repository: "); + message.append(alias); + LOG_LOCATION(message.c_str()); + if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; @@ -566,6 +634,7 @@ } unsigned packages_to_install(struct Zypp *zypp) noexcept { + LOG_LOCATION("Getting number of packages to install"); return zypp::ResPool::instance() .byStatus(&zypp::ResStatus::isToBeInstalled) .size(); @@ -573,6 +642,14 @@ static bool package_check(Zypp *zypp, const char *tag, bool selected, Status *status) noexcept { + std::string message("Checking package: "); + message.append(tag); + if (selected) + message.append(" (selected)"); + else + message.append(" (available)"); + LOG_LOCATION(message.c_str()); + try { std::string s_tag(tag); if (s_tag.empty()) { @@ -615,6 +692,13 @@ void add_repository(struct Zypp *zypp, const char *alias, const char *url, struct Status *status, ZyppProgressCallback callback, void *user_data) noexcept { + std::string message("Adding repository: "); + message.append(alias); + message.append(" ("); + message.append(filter_url(url)); + message.append(")"); + LOG_LOCATION(message.c_str()); + if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; @@ -634,6 +718,10 @@ void disable_repository(struct Zypp *zypp, const char *alias, struct Status *status) noexcept { + std::string message("Disabling repository: "); + message.append(alias); + LOG_LOCATION(message.c_str()); + if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; @@ -650,6 +738,14 @@ void set_repository_url(struct Zypp *zypp, const char *alias, const char *url, struct Status *status) noexcept { + + std::string message("Setting repository url: "); + message.append(alias); + message.append(" ("); + message.append(filter_url(url)); + message.append(")"); + LOG_LOCATION(message.c_str()); + if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; @@ -668,6 +764,10 @@ void remove_repository(struct Zypp *zypp, const char *alias, struct Status *status, ZyppProgressCallback callback, void *user_data) noexcept { + std::string message("Removing repository: "); + message.append(alias); + LOG_LOCATION(message.c_str()); + if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; @@ -687,6 +787,8 @@ struct RepositoryList list_repositories(struct Zypp *zypp, struct Status *status) noexcept { + LOG_LOCATION("Listing repositories"); + if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return {0, NULL}; @@ -715,6 +817,10 @@ void load_repository_cache(struct Zypp *zypp, const char *alias, struct Status *status) noexcept { + std::string message("Loading repository cache: "); + message.append(alias); + LOG_LOCATION(message.c_str()); + if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); } @@ -739,6 +845,10 @@ struct Status *status, ZyppProgressCallback callback, void *user_data) noexcept { + std::string message("Building repository cache: "); + message.append(alias); + LOG_LOCATION(message.c_str()); + if (zypp->repo_manager == NULL) { STATUS_ERROR(status, "Internal Error: Repo manager is not initialized."); return; @@ -762,6 +872,10 @@ void import_gpg_key(struct Zypp *zypp, const char *const pathname, struct Status *status) noexcept { + std::string message("Importing GPG key: "); + message.append(pathname); + LOG_LOCATION(message.c_str()); + try { zypp::filesystem::Pathname path(pathname); zypp::PublicKey key(path); @@ -779,6 +893,8 @@ void get_space_usage(struct Zypp *zypp, struct Status *status, struct MountPoint *mount_points, unsigned mount_points_size) noexcept { + LOG_LOCATION("Getting disk space usage"); + try { zypp::DiskUsageCounter::MountPointSet mount_points_set; for (unsigned i = 0; i < mount_points_size; ++i) { ++++++ agama.obsinfo ++++++ --- /var/tmp/diff_new_pack.OZ1gw2/_old 2026-04-01 19:55:34.421445361 +0200 +++ /var/tmp/diff_new_pack.OZ1gw2/_new 2026-04-01 19:55:34.437446021 +0200 @@ -1,5 +1,5 @@ name: agama -version: 19+0.e4e1132bc -mtime: 1773756524 -commit: e4e1132bc3d19d48ebf6e423d089cce0e78ecf3e +version: 19+70.3f126ff03 +mtime: 1774366607 +commit: 3f126ff037d4587f7c4a1aff0042f0ac2f0c1f21 ++++++ vendor.tar.zst ++++++ /work/SRC/openSUSE:Factory/agama/vendor.tar.zst /work/SRC/openSUSE:Factory/.agama.new.21863/vendor.tar.zst differ: char 7, line 1
