diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 5ddab71477..ea440e35db 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -55,7 +55,7 @@ dependencies = [ "chrono", "clap", "console", - "fluent-uri 0.3.2", + "fluent-uri 0.4.1", "home", "indicatif", "inquire", @@ -154,7 +154,7 @@ dependencies = [ "chrono", "curl", "env_logger", - "fluent-uri 0.3.2", + "fluent-uri 0.4.1", "fs_extra", "futures-util", "home", diff --git a/rust/agama-cli/Cargo.toml b/rust/agama-cli/Cargo.toml index 814542d7c0..7574deb90e 100644 --- a/rust/agama-cli/Cargo.toml +++ b/rust/agama-cli/Cargo.toml @@ -27,7 +27,7 @@ inquire = { version = "0.7.5", default-features = false, features = [ chrono = "0.4.38" regex = "1.11.1" home = "0.5.11" -fluent-uri = "0.3.2" +fluent-uri = "0.4.1" serde = { version = "1.0.228", features = ["derive"] } [[bin]] diff --git a/rust/agama-cli/src/config.rs b/rust/agama-cli/src/config.rs index b1db9d79c5..fda210caa2 100644 --- a/rust/agama-cli/src/config.rs +++ b/rust/agama-cli/src/config.rs @@ -21,7 +21,6 @@ use std::{io::Write, path::PathBuf, process::Command, time::Duration}; use agama_lib::{ - context::InstallationContext, http::BaseHTTPClient, monitor::MonitorClient, profile::{ProfileHTTPClient, ProfileValidator, ValidationOutcome}, @@ -38,7 +37,7 @@ use tokio::time::sleep; use crate::{ api_url, build_clients, build_http_client, cli_input::CliInput, cli_output::CliOutput, - show_progress, GlobalOpts, + context::InstallationContext, show_progress, GlobalOpts, }; const DEFAULT_EDITOR: &str = "/usr/bin/vi"; @@ -253,7 +252,7 @@ async fn generate( url_or_path: CliInput, insecure: bool, ) -> anyhow::Result<()> { - let _context = match &url_or_path { + let context = match &url_or_path { CliInput::Stdin | CliInput::Full(_) => InstallationContext::from_env()?, CliInput::Url(url_str) => InstallationContext::from_url_str(url_str)?, CliInput::Path(pathbuf) => InstallationContext::from_file(pathbuf.as_path())?, @@ -266,7 +265,7 @@ async fn generate( // AutoYaST specific download and convert to JSON let config_string = match url_or_path { CliInput::Url(url_string) => { - let url = Uri::parse(url_string)?; + let url = Uri::parse(url_string).map_err(|(e, _)| e)?; ProfileHTTPClient::new(client.clone()) .from_autoyast(&url) @@ -275,7 +274,7 @@ async fn generate( CliInput::Path(pathbuf) => { let canon_path = pathbuf.canonicalize()?; let url_string = format!("file://{}", canon_path.display()); - let url = Uri::parse(url_string)?; + let url = Uri::parse(url_string).map_err(|(e, _)| e)?; ProfileHTTPClient::new(client.clone()) .from_autoyast(&url) @@ -297,9 +296,8 @@ async fn generate( return Ok(()); } - // TODO: resolves relative URL references - let model: api::Config = serde_json::from_str(&profile_json)?; - let config_json = serde_json::to_string_pretty(&model)?; + let config = api::Config::from_json(&profile_json, &context.source)?; + let config_json = serde_json::to_string_pretty(&config)?; println!("{}", &config_json); let validity = validate(client, CliInput::Full(config_json.clone()), false).await?; diff --git a/rust/agama-lib/src/context.rs b/rust/agama-cli/src/context.rs similarity index 100% rename from rust/agama-lib/src/context.rs rename to rust/agama-cli/src/context.rs diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 6487364019..3a1137b6f3 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -28,7 +28,6 @@ use std::{ use agama_lib::{ auth::AuthToken, - context::InstallationContext, error::ServiceError, http::{BaseHTTPClient, WebSocketClient}, manager::ManagerHTTPClient, @@ -54,12 +53,15 @@ mod cli_input; mod cli_output; mod commands; mod config; +mod context; mod error; mod events; mod logs; mod progress; mod questions; +use context::InstallationContext; + /// Agama's CLI global options #[derive(Args, Clone)] pub struct GlobalOpts { diff --git a/rust/agama-files/src/service.rs b/rust/agama-files/src/service.rs index 573f048baa..c432b6664e 100644 --- a/rust/agama-files/src/service.rs +++ b/rust/agama-files/src/service.rs @@ -211,8 +211,14 @@ impl MessageHandler for Service { let to_run = scripts.by_group(message.group).clone(); if to_run.is_empty() { + tracing::info!("No scripts to run in group {}", message.group.to_string()); return Ok(false); } else { + tracing::info!( + "{} scripts to run in group {}", + to_run.len(), + message.group.to_string() + ); let runner = ScriptsRunner::new( &self.root_dir, &self.install_dir, @@ -232,7 +238,9 @@ impl MessageHandler for Service { impl MessageHandler for Service { async fn handle(&mut self, _message: message::WriteFiles) -> Result<(), Error> { for file in &self.files { - file.write(&self.install_dir).await?; + if let Err(error) = file.write(&self.install_dir).await { + tracing::error!("Failed to write file {}: {error}", file.destination); + } } Ok(()) } diff --git a/rust/agama-lib/Cargo.toml b/rust/agama-lib/Cargo.toml index a75bcee906..2bc171645c 100644 --- a/rust/agama-lib/Cargo.toml +++ b/rust/agama-lib/Cargo.toml @@ -44,7 +44,7 @@ strum = { version = "0.27.1", features = ["derive"] } fs_extra = "1.3.0" serde_with = "3.12.0" regex = "1.11.1" -fluent-uri = { version = "0.3.2", features = ["serde"] } +fluent-uri = { version = "0.4.1", features = ["serde"] } tokio-tungstenite = { version = "0.26.2", features = ["native-tls"] } tokio-native-tls = "0.3.1" percent-encoding = "2.3.1" diff --git a/rust/agama-lib/src/install_settings.rs b/rust/agama-lib/src/install_settings.rs index 6088d77472..8493dacddf 100644 --- a/rust/agama-lib/src/install_settings.rs +++ b/rust/agama-lib/src/install_settings.rs @@ -21,14 +21,12 @@ //! Configuration settings handling //! //! This module implements the mechanisms to load and store the installation settings. -use crate::context::InstallationContext; use crate::hostname::model::HostnameSettings; use crate::storage::settings::zfcp::ZFCPConfig; use crate::{network::NetworkSettings, storage::settings::dasd::DASDConfig}; use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; use std::default::Default; -use std::path::Path; #[derive(Debug, thiserror::Error)] pub enum InstallSettingsError { @@ -64,27 +62,3 @@ pub struct InstallSettings { #[serde(skip_serializing_if = "Option::is_none")] pub zfcp: Option, } - -impl InstallSettings { - /// Returns install settings from a file. - pub fn from_file>( - path: P, - context: &InstallationContext, - ) -> Result { - let content = std::fs::read_to_string(path)?; - Ok(Self::from_json(&content, context)?) - } - - /// Reads install settings from a JSON string, - /// also resolving relative URLs in the contents. - /// - /// - `json`: JSON string. - /// - `context`: Store context. - pub fn from_json( - json: &str, - _context: &InstallationContext, - ) -> Result { - let settings: InstallSettings = serde_json::from_str(json)?; - Ok(settings) - } -} diff --git a/rust/agama-lib/src/lib.rs b/rust/agama-lib/src/lib.rs index 4e3a22d3bd..725c20c22e 100644 --- a/rust/agama-lib/src/lib.rs +++ b/rust/agama-lib/src/lib.rs @@ -44,7 +44,6 @@ //! As said, those modules might implement additional stuff, like specific types, clients, etc. pub mod auth; -pub mod context; pub mod error; pub mod hostname; pub mod http; diff --git a/rust/agama-utils/src/api/config.rs b/rust/agama-utils/src/api/config.rs index f837ecbe5b..aa132ac3ae 100644 --- a/rust/agama-utils/src/api/config.rs +++ b/rust/agama-utils/src/api/config.rs @@ -19,14 +19,25 @@ // find current contact information at www.suse.com. use crate::api::{ - bootloader, files, hostname, iscsi, l10n, network, proxy, question, s390, security, + bootloader, + files::{self, FileSourceError}, + hostname, iscsi, l10n, network, proxy, question, s390, security, software::{self, ProductConfig}, storage, users, }; +use fluent_uri::Uri; use merge::Merge; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to resolve relative URLs")] + ResolveURL(#[from] FileSourceError), + #[error("Failed to parse the configuration")] + JSON(#[from] serde_json::Error), +} + #[skip_serializing_none] #[derive(Clone, Debug, Default, Deserialize, Serialize, Merge, utoipa::ToSchema)] #[serde(rename_all = "camelCase")] @@ -55,6 +66,19 @@ pub struct Config { } impl Config { + /// Reads install settings from a JSON string, resolving relative URLs in the contents. + /// + /// - `json`: JSON string. + /// - `base_uri`: base URI. + pub fn from_json(json: &str, base_uri: &Uri) -> Result { + let mut config: Self = serde_json::from_str(json)?; + if let Some(files) = &mut config.files { + files.resolve_urls(base_uri)?; + } + Ok(config) + } + + /// Creates a default configuration for the given product. pub fn with_product(product_id: String) -> Self { Self { software: Some(software::Config { diff --git a/rust/agama-utils/src/api/files/config.rs b/rust/agama-utils/src/api/files/config.rs index fe16e7793c..3be239bf8a 100644 --- a/rust/agama-utils/src/api/files/config.rs +++ b/rust/agama-utils/src/api/files/config.rs @@ -38,6 +38,18 @@ pub struct Config { pub scripts: Option, } +impl Config { + pub fn resolve_urls(&mut self, base_uri: &Uri) -> Result<(), FileSourceError> { + resolve_urls_for(&mut self.files, base_uri)?; + + if let Some(scripts) = &mut self.scripts { + scripts.resolve_urls(base_uri)?; + } + + Ok(()) + } +} + #[derive(Clone, Debug, Default, Serialize, Deserialize, Merge, utoipa::ToSchema, PartialEq)] #[serde(rename_all = "camelCase")] #[merge(strategy = merge::option::overwrite_none)] @@ -73,24 +85,24 @@ impl ScriptsConfig { /// /// * `base_uri`: The base URI to resolve relative URLs against. pub fn resolve_urls(&mut self, base_uri: &Uri) -> Result<(), FileSourceError> { - Self::resolve_urls_for(&mut self.pre, base_uri)?; - Self::resolve_urls_for(&mut self.post_partitioning, base_uri)?; - Self::resolve_urls_for(&mut self.post, base_uri)?; - Self::resolve_urls_for(&mut self.init, base_uri)?; + resolve_urls_for(&mut self.pre, base_uri)?; + resolve_urls_for(&mut self.post_partitioning, base_uri)?; + resolve_urls_for(&mut self.post, base_uri)?; + resolve_urls_for(&mut self.init, base_uri)?; Ok(()) } +} - fn resolve_urls_for( - scripts: &mut Option>, - base_uri: &Uri, - ) -> Result<(), FileSourceError> { - if let Some(ref mut scripts) = scripts { - for script in scripts { - script.resolve_url(&base_uri)?; - } +fn resolve_urls_for( + files: &mut Option>, + base_uri: &Uri, +) -> Result<(), FileSourceError> { + if let Some(ref mut files) = files { + for file in files { + file.resolve_url(&base_uri)?; } - Ok(()) } + Ok(()) } #[cfg(test)] diff --git a/rust/package/agama.changes b/rust/package/agama.changes index faa65c590b..43dbf88460 100644 --- a/rust/package/agama.changes +++ b/rust/package/agama.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Feb 13 08:47:07 UTC 2026 - Imobach Gonzalez Sosa + +- Resolve the relative URLs (bsc#1258118). +- Do not finish the installation as "failed" if it was not possible + to fetch the user files (bsc#1258118). + ------------------------------------------------------------------- Thu Feb 12 14:43:48 UTC 2026 - José Iván López González