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
26 changes: 24 additions & 2 deletions rust/agama-software/src/model/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ pub enum RegistrationError {
AddService(String, #[source] zypp_agama::ZyppError),
#[error("Failed to refresh the service {0}: {1}")]
RefreshService(String, #[source] zypp_agama::ZyppError),
#[error("Failed to select product from service {0}: {1}")]
SelectProduct(String, #[source] zypp_agama::ZyppError),
#[error("Failed to copy file {0}: {1}")]
IO(String, #[source] std::io::Error),
#[error(transparent)]
Expand Down Expand Up @@ -148,9 +150,20 @@ impl Registration {
zypp.add_service(&service.name, &service.url)
.map_err(|e| RegistrationError::AddService(service.name.clone(), e))?;
let name = service.name.clone();
self.services.push(service);
self.services.push(service.clone());
zypp.refresh_service(&name)
.map_err(|e| RegistrationError::RefreshService(name, e))?;
.map_err(|e| RegistrationError::RefreshService(name.clone(), e))?;
zypp.load_source(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the repository load fix, the rest is about selecting the add-on products. 😃

zypp_agama::callbacks::empty_progress,
&mut zypp_agama::callbacks::security::EmptyCallback,
)
.map_err(|e| RegistrationError::RefreshService(name, e))?;

// skip for the first service (base product), the base product is selected differently
if self.services.len() > 1 {
zypp.select_products_from_service(&service.name)
.map_err(|e| RegistrationError::SelectProduct(service.name.clone(), e))?;
}
Ok(())
}

Expand Down Expand Up @@ -268,6 +281,15 @@ impl Registration {
pub fn base_product_service_name(&self) -> Option<String> {
self.services.first().map(|s| s.name.clone())
}

pub fn addon_product_service_names(&self) -> Vec<String> {
self.services
.iter()
// skip the first service (base product)
.skip(1)
.map(|s| s.name.clone())
.collect()
}
}

/// A builder for a [Registration] object.
Expand Down
8 changes: 8 additions & 0 deletions rust/agama-software/src/zypp_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,14 @@ impl ZyppServer {
};
}

// if registered select products from add-on services
if let RegistrationStatus::Registered(boxed_registration) = &self.registration {
let registration = boxed_registration.as_ref();
for name in registration.addon_product_service_names() {
zypp.select_products_from_service(&name)?;
}
}

self.only_required = state.options.only_required;
tracing::info!("Install only required packages: {}", self.only_required);
// run the solver to select the dependencies, ignore the errors, the solver runs again later
Expand Down
8 changes: 8 additions & 0 deletions rust/package/agama.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
-------------------------------------------------------------------
Thu Mar 5 07:19:01 UTC 2026 - Ladislav Slezák <lslezak@suse.com>

- Refresh and load the repositories after registration
(bsc#1259217)
- Select addon product to install after registering an extension
(bsc#1258187)

-------------------------------------------------------------------
Thu Mar 5 07:14:07 UTC 2026 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

Expand Down
39 changes: 39 additions & 0 deletions rust/zypp-agama/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
};

use errors::ZyppResult;
use tracing::info;
use zypp_agama_sys::{get_patterns, ProgressCallback, ProgressData, Status, ZyppProgressCallback};

pub mod errors;
Expand Down Expand Up @@ -337,6 +338,40 @@ impl Zypp {
}
}

pub fn select_products_from_service(&self, service: &String) -> ZyppResult<()> {
unsafe {
let mut status: Status = Status::default();
let status_ptr = &mut status as *mut _;
info!("Selecting products from service {}", service);

// find products from the requested service
let products = zypp_agama_sys::get_products(self.ptr, status_ptr);
for i in 0..products.size as usize {
let c_product = *(products.list.add(i));

if string_from_ptr(c_product.service_alias) == *service {
let prod_name = string_from_ptr(c_product.name);
let repo_alias = string_from_ptr(c_product.repo_alias);
info!(
"Selecting product {} from repository {}",
prod_name, repo_alias
);
zypp_agama_sys::resolvable_select(
self.ptr,
c_product.name,
zypp_agama_sys::RESOLVABLE_KIND_RESOLVABLE_PRODUCT,
ResolvableSelected::Installation.into(),
status_ptr,
);
}
}

zypp_agama_sys::free_products(&products);

helpers::status_to_result_void(status)
}
}

pub fn unselect_resolvable(
&self,
name: &str,
Expand Down Expand Up @@ -565,6 +600,7 @@ impl Zypp {
{
let repos = self.list_repositories()?;
let enabled_repos: Vec<&Repository> = repos.iter().filter(|r| r.enabled).collect();
info!("Found {} enabled repositories", enabled_repos.len());
// TODO: this step logic for progress can be enclosed to own struct
let mut percent: f64 = 0.0;
let percent_step: f64 = 100.0 / (enabled_repos.len() as f64 * 3.0); // 3 substeps
Expand All @@ -579,6 +615,7 @@ impl Zypp {
return abort_err;
}

info!("Refreshing repository {}", &i.alias);
self.refresh_repository(
&i.alias,
&callbacks::download_progress::EmptyCallback,
Expand All @@ -592,6 +629,7 @@ impl Zypp {
if !cont {
return abort_err;
}
info!("Creating repository cache for {}", &i.alias);
self.create_repo_cache(&i.alias, callbacks::empty_progress)?;
percent += percent_step;
cont = progress(
Expand All @@ -601,6 +639,7 @@ impl Zypp {
if !cont {
return abort_err;
}
info!("Loading repository cache for {}", &i.alias);
self.load_repo_cache(&i.alias)?;
percent += percent_step;
}
Expand Down
18 changes: 18 additions & 0 deletions rust/zypp-agama/zypp-agama-sys/c-layer/include/lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,24 @@ struct Patterns get_patterns(struct Zypp *_zypp,
struct Status *status) noexcept;
void free_patterns(const struct Patterns *patterns) noexcept;

/// Representation of zypp::Product
struct Product {
// so far we do not need more details about the products
const char *name; ///< owned
const char *repo_alias; ///< owned
const char *service_alias; ///< owned
};

struct Products {
struct Product *list; ///< owned, *size* items
unsigned size;
};

/// Get Product details.
struct Products get_products(struct Zypp *_zypp,
struct Status *status) noexcept;
void free_products(const struct Products *products) noexcept;

void import_gpg_key(struct Zypp *zypp, const char *const pathname,
struct Status *status) noexcept;

Expand Down
35 changes: 35 additions & 0 deletions rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <zypp-core/Url.h>
#include <zypp/DiskUsageCounter.h>
#include <zypp/Pattern.h>
#include <zypp/Product.h>
#include <zypp/PublicKey.h>
#include <zypp/RepoInfo.h>
#include <zypp/RepoManager.h>
Expand Down Expand Up @@ -422,6 +423,40 @@ void free_patterns(const struct Patterns *patterns) noexcept {
free((void *)patterns->list);
}

struct Products get_products(struct Zypp *zypp,
struct Status *status) noexcept {
auto iterator =
zypp->zypp_pointer->pool().proxy().byKind(zypp::ResKind::product);

Products result = {
(struct Product *)malloc(iterator.size() * sizeof(Product)),
0 // initialize with zero and increase after each successful add of
// product info
};

for (const auto iter : iterator) {
Product &product = result.list[result.size];
auto zypp_product = iter->candidateAsKind<zypp::Product>();
product.name = strdup(iter->name().c_str());
product.repo_alias = strdup(zypp_product->repoInfo().alias().c_str());
product.service_alias = strdup(zypp_product->repoInfo().service().c_str());
result.size++;
}

STATUS_OK(status);
return result;
}

void free_products(const struct Products *products) noexcept {
for (unsigned i = 0; i < products->size; ++i) {
free((void *)products->list[i].name);
free((void *)products->list[i].repo_alias);
free((void *)products->list[i].service_alias);
}

free((void *)products->list);
}

bool run_solver(struct Zypp *zypp, bool only_required,
struct Status *status) noexcept {
try {
Expand Down
37 changes: 37 additions & 0 deletions rust/zypp-agama/zypp-agama-sys/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,40 @@ const _: () = {
["Offset of field: Patterns::list"][::std::mem::offset_of!(Patterns, list) - 0usize];
["Offset of field: Patterns::size"][::std::mem::offset_of!(Patterns, size) - 8usize];
};
#[doc = " Representation of zypp::Product"]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Product {
#[doc = "< owned"]
pub name: *const ::std::os::raw::c_char,
#[doc = "< owned"]
pub repo_alias: *const ::std::os::raw::c_char,
#[doc = "< owned"]
pub service_alias: *const ::std::os::raw::c_char,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
["Size of Product"][::std::mem::size_of::<Product>() - 24usize];
["Alignment of Product"][::std::mem::align_of::<Product>() - 8usize];
["Offset of field: Product::name"][::std::mem::offset_of!(Product, name) - 0usize];
["Offset of field: Product::repo_alias"][::std::mem::offset_of!(Product, repo_alias) - 8usize];
["Offset of field: Product::service_alias"]
[::std::mem::offset_of!(Product, service_alias) - 16usize];
};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Products {
#[doc = "< owned, *size* items"]
pub list: *mut Product,
pub size: ::std::os::raw::c_uint,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
["Size of Products"][::std::mem::size_of::<Products>() - 16usize];
["Alignment of Products"][::std::mem::align_of::<Products>() - 8usize];
["Offset of field: Products::list"][::std::mem::offset_of!(Products, list) - 0usize];
["Offset of field: Products::size"][::std::mem::offset_of!(Products, size) - 8usize];
};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Repository {
Expand Down Expand Up @@ -616,6 +650,9 @@ unsafe extern "C" {
#[doc = " Get Pattern details.\n Unknown patterns are simply omitted from the result. Match by\n PatternInfo.name, not by index."]
pub fn get_patterns(_zypp: *mut Zypp, status: *mut Status) -> Patterns;
pub fn free_patterns(patterns: *const Patterns);
#[doc = " Get Product details."]
pub fn get_products(_zypp: *mut Zypp, status: *mut Status) -> Products;
pub fn free_products(products: *const Products);
pub fn import_gpg_key(
zypp: *mut Zypp,
pathname: *const ::std::os::raw::c_char,
Expand Down
Loading