Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rust/agama-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down
14 changes: 6 additions & 8 deletions rust/agama-cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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";
Expand Down Expand Up @@ -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())?,
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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?;
Expand Down
File renamed without changes.
4 changes: 3 additions & 1 deletion rust/agama-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ use std::{

use agama_lib::{
auth::AuthToken,
context::InstallationContext,
error::ServiceError,
http::{BaseHTTPClient, WebSocketClient},
manager::ManagerHTTPClient,
Expand All @@ -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 {
Expand Down
10 changes: 9 additions & 1 deletion rust/agama-files/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,14 @@ impl MessageHandler<message::RunScripts> 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,
Expand All @@ -232,7 +238,9 @@ impl MessageHandler<message::RunScripts> for Service {
impl MessageHandler<message::WriteFiles> 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(())
}
Expand Down
2 changes: 1 addition & 1 deletion rust/agama-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
26 changes: 0 additions & 26 deletions rust/agama-lib/src/install_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -64,27 +62,3 @@ pub struct InstallSettings {
#[serde(skip_serializing_if = "Option::is_none")]
pub zfcp: Option<ZFCPConfig>,
}

impl InstallSettings {
/// Returns install settings from a file.
pub fn from_file<P: AsRef<Path>>(
path: P,
context: &InstallationContext,
) -> Result<Self, InstallSettingsError> {
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<Self, InstallSettingsError> {
let settings: InstallSettings = serde_json::from_str(json)?;
Ok(settings)
}
}
1 change: 0 additions & 1 deletion rust/agama-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 25 additions & 1 deletion rust/agama-utils/src/api/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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<String>) -> Result<Self, Error> {
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 {
Expand Down
38 changes: 25 additions & 13 deletions rust/agama-utils/src/api/files/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ pub struct Config {
pub scripts: Option<ScriptsConfig>,
}

impl Config {
pub fn resolve_urls(&mut self, base_uri: &Uri<String>) -> Result<(), FileSourceError> {
resolve_urls_for(&mut self.files, base_uri)?;

if let Some(scripts) = &mut self.scripts {
Comment thread
jreidinger marked this conversation as resolved.
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)]
Expand Down Expand Up @@ -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<String>) -> 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<T: WithFileSource>(
scripts: &mut Option<Vec<T>>,
base_uri: &Uri<String>,
) -> Result<(), FileSourceError> {
if let Some(ref mut scripts) = scripts {
for script in scripts {
script.resolve_url(&base_uri)?;
}
fn resolve_urls_for<T: WithFileSource>(
files: &mut Option<Vec<T>>,
base_uri: &Uri<String>,
) -> Result<(), FileSourceError> {
if let Some(ref mut files) = files {
for file in files {
file.resolve_url(&base_uri)?;
}
Ok(())
}
Ok(())
}

#[cfg(test)]
Expand Down
7 changes: 7 additions & 0 deletions rust/package/agama.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
-------------------------------------------------------------------
Fri Feb 13 08:47:07 UTC 2026 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

- 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 <jlopez@suse.com>

Expand Down
Loading