deb: drive the cross-build repository setup from the distro data
This commit is contained in:
+114
-59
@@ -67,8 +67,41 @@ pub fn setup_environment(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The suites a cross-build environment enables for `series`: the series
|
||||
/// itself plus the distro data's cross pockets (`<series>-updates`,
|
||||
/// `<series>-backports`, `<series>-security`), plus the explicitly
|
||||
/// requested `pocket` when one is given ('proposed' stays opt-in exactly
|
||||
/// this way — it is not a cross pocket). Shared by the source-adjusting
|
||||
/// pass and the added mirror entry, which used to duplicate the list.
|
||||
fn cross_suites(
|
||||
series: &str,
|
||||
pocket: Option<&str>,
|
||||
dist: &str,
|
||||
) -> Result<Vec<String>, Box<dyn Error>> {
|
||||
let mut suites = vec![series.to_string()];
|
||||
for p in crate::distro_info::get_cross_pockets(dist)? {
|
||||
suites.push(format!("{series}-{p}"));
|
||||
}
|
||||
if let Some(p) = pocket {
|
||||
let pocket_suite = format!("{series}-{p}");
|
||||
if !suites.contains(&pocket_suite) {
|
||||
suites.push(pocket_suite);
|
||||
}
|
||||
}
|
||||
Ok(suites)
|
||||
}
|
||||
|
||||
/// Ensure that repositories for target architecture are available
|
||||
/// This also handles the 'ports.ubuntu.com' vs 'archive.ubuntu.com' on Ubuntu
|
||||
///
|
||||
/// On Ubuntu hosts, driven by the bundled distro data
|
||||
/// (`data/distro_info.yml`): the official sources served by the mirror of
|
||||
/// the local architecture (the primary archive and its security sibling)
|
||||
/// are scoped to it and carry every component and cross-build suite, and
|
||||
/// the mirror serving the target architecture (ports, for the non-local
|
||||
/// ones) is added when no existing source serves the arch from it yet.
|
||||
/// Debian hosts are left alone: one mirror serves every architecture, so
|
||||
/// the host's own sources already cover the target — the os-release gate
|
||||
/// below is what makes that a data conclusion instead of hardcoding.
|
||||
pub fn ensure_repositories(
|
||||
arch: &str,
|
||||
series: &str,
|
||||
@@ -97,90 +130,78 @@ pub fn ensure_repositories(
|
||||
if !os_release.contains("ID=ubuntu") {
|
||||
return Ok(());
|
||||
}
|
||||
let dist = "ubuntu";
|
||||
|
||||
// Load existing sources
|
||||
let mut sources = crate::apt::sources::load(Some(ctx.clone()))?;
|
||||
|
||||
// The mirrors serving each side of the cross build (primary for the
|
||||
// local architectures, ports for the others) and the distro data's
|
||||
// components and suites
|
||||
let local_mirror = crate::distro_info::mirror_for_arch(dist, &local_arch)?;
|
||||
let target_mirror = crate::distro_info::mirror_for_arch(dist, arch)?;
|
||||
let components = crate::distro_info::get_dist_components(dist)?;
|
||||
let required_suites = cross_suites(series, pocket, dist)?;
|
||||
|
||||
// Ensure all components are enabled for the primary architecture
|
||||
for source in &mut sources {
|
||||
if source.uri.contains("archive.ubuntu.com") || source.uri.contains("security.ubuntu.com") {
|
||||
// Scope to local_arch if not already scoped
|
||||
if source.architectures.is_empty() {
|
||||
source.architectures.push(local_arch.clone());
|
||||
}
|
||||
// Official sources served by the local mirror (the primary archive
|
||||
// and its security sibling); ports serves the other architectures
|
||||
// and is configured below instead
|
||||
if !crate::distro_info::is_mirror_source(local_mirror, &source.uri) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure all components are present
|
||||
let required_components = ["main", "restricted", "universe", "multiverse"];
|
||||
for &comp in &required_components {
|
||||
if !source.components.contains(&comp.to_string()) {
|
||||
source.components.push(comp.to_string());
|
||||
}
|
||||
}
|
||||
// Scope to local_arch if not already scoped
|
||||
if source.architectures.is_empty() {
|
||||
source.architectures.push(local_arch.clone());
|
||||
}
|
||||
|
||||
// Ensure all suites (pockets) are enabled, excluding 'proposed'
|
||||
// unless explicitly requested through the 'pocket' option
|
||||
let mut required_suites = vec![
|
||||
series.to_string(),
|
||||
format!("{}-updates", series),
|
||||
format!("{}-backports", series),
|
||||
format!("{}-security", series),
|
||||
];
|
||||
if let Some(p) = pocket {
|
||||
let pocket_suite = format!("{series}-{p}");
|
||||
if !required_suites.contains(&pocket_suite) {
|
||||
required_suites.push(pocket_suite);
|
||||
}
|
||||
// Ensure all components are present
|
||||
for comp in &components {
|
||||
if !source.components.contains(comp) {
|
||||
source.components.push(comp.clone());
|
||||
}
|
||||
for suite in required_suites {
|
||||
if !source.suite.contains(&suite) {
|
||||
source.suite.push(suite);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure all suites (pockets) are enabled
|
||||
for suite in &required_suites {
|
||||
if !source.suite.contains(suite) {
|
||||
source.suite.push(suite.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if ports repository already exists for the target architecture
|
||||
let has_ports = sources
|
||||
.iter()
|
||||
.any(|s| s.uri.contains("ports.ubuntu.com") && s.architectures.contains(&arch.to_string()));
|
||||
// Check whether an existing source already serves the target
|
||||
// architecture from its mirror (e.g. the ports mirror for a
|
||||
// non-local arch); when cross-building for the local architecture,
|
||||
// the primary sources above already do
|
||||
let has_target = sources.iter().any(|s| {
|
||||
crate::distro_info::is_mirror_source(target_mirror, &s.uri)
|
||||
&& s.architectures.contains(&arch.to_string())
|
||||
});
|
||||
|
||||
if !has_ports {
|
||||
// Add ports repository for the target architecture
|
||||
let mut ports_suites = vec![
|
||||
series.to_string(),
|
||||
format!("{series}-updates"),
|
||||
format!("{series}-backports"),
|
||||
format!("{series}-security"),
|
||||
];
|
||||
if let Some(p) = pocket {
|
||||
let pocket_suite = format!("{series}-{p}");
|
||||
if !ports_suites.contains(&pocket_suite) {
|
||||
ports_suites.push(pocket_suite);
|
||||
}
|
||||
}
|
||||
let ports_entry = crate::apt::sources::SourceEntry {
|
||||
if !has_target {
|
||||
// Add the target architecture's mirror (ports for the non-local
|
||||
// architectures on Ubuntu)
|
||||
let mirror_entry = crate::apt::sources::SourceEntry {
|
||||
enabled: true,
|
||||
kind: crate::apt::sources::SourceKind::Deb,
|
||||
components: vec![
|
||||
"main".to_string(),
|
||||
"restricted".to_string(),
|
||||
"universe".to_string(),
|
||||
"multiverse".to_string(),
|
||||
],
|
||||
components: components.clone(),
|
||||
architectures: vec![arch.to_string()],
|
||||
uri: "http://ports.ubuntu.com/ubuntu-ports".to_string(),
|
||||
uri: target_mirror.url.clone(),
|
||||
signed_by: None,
|
||||
trusted: None,
|
||||
suite: ports_suites,
|
||||
suite: required_suites.clone(),
|
||||
// No origin: saved to the pkh-owned added-sources file
|
||||
origin: None,
|
||||
};
|
||||
sources.push(ports_entry);
|
||||
sources.push(mirror_entry);
|
||||
}
|
||||
|
||||
// Save the updated sources: each entry is written back to its origin
|
||||
// file in its own format (keeping its own Signed-By and Enabled state),
|
||||
// and the new ports entry goes to the pkh-owned added-sources file
|
||||
// and the new mirror entry goes to the pkh-owned added-sources file
|
||||
crate::apt::sources::save(Some(ctx.clone()), sources)?;
|
||||
|
||||
Ok(())
|
||||
@@ -234,4 +255,38 @@ mod tests {
|
||||
);
|
||||
assert_eq!(env.len(), 2);
|
||||
}
|
||||
|
||||
/// The suite list a cross-build environment enables: the series, its
|
||||
/// cross pockets from the distro data (updates, backports, security),
|
||||
/// and the explicitly requested pocket — which is the only way
|
||||
/// 'proposed' gets in.
|
||||
#[test]
|
||||
fn test_cross_suites_from_distro_data() {
|
||||
assert_eq!(
|
||||
cross_suites("noble", None, "ubuntu").unwrap(),
|
||||
vec![
|
||||
"noble".to_string(),
|
||||
"noble-updates".to_string(),
|
||||
"noble-backports".to_string(),
|
||||
"noble-security".to_string()
|
||||
]
|
||||
);
|
||||
// An explicitly requested pocket is added (not duplicated when it
|
||||
// is already a cross pocket).
|
||||
assert_eq!(
|
||||
cross_suites("noble", Some("proposed"), "ubuntu").unwrap(),
|
||||
vec![
|
||||
"noble".to_string(),
|
||||
"noble-updates".to_string(),
|
||||
"noble-backports".to_string(),
|
||||
"noble-security".to_string(),
|
||||
"noble-proposed".to_string()
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
cross_suites("noble", Some("updates"), "ubuntu").unwrap(),
|
||||
cross_suites("noble", None, "ubuntu").unwrap()
|
||||
);
|
||||
assert!(cross_suites("noble", None, "not-a-distro").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user