deb: drive the cross-build repository setup from the distro data
This commit is contained in:
+108
-53
@@ -67,8 +67,41 @@ pub fn setup_environment(
|
|||||||
Ok(())
|
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
|
/// 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(
|
pub fn ensure_repositories(
|
||||||
arch: &str,
|
arch: &str,
|
||||||
series: &str,
|
series: &str,
|
||||||
@@ -97,90 +130,78 @@ pub fn ensure_repositories(
|
|||||||
if !os_release.contains("ID=ubuntu") {
|
if !os_release.contains("ID=ubuntu") {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
let dist = "ubuntu";
|
||||||
|
|
||||||
// Load existing sources
|
// Load existing sources
|
||||||
let mut sources = crate::apt::sources::load(Some(ctx.clone()))?;
|
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
|
// Ensure all components are enabled for the primary architecture
|
||||||
for source in &mut sources {
|
for source in &mut sources {
|
||||||
if source.uri.contains("archive.ubuntu.com") || source.uri.contains("security.ubuntu.com") {
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
// Scope to local_arch if not already scoped
|
// Scope to local_arch if not already scoped
|
||||||
if source.architectures.is_empty() {
|
if source.architectures.is_empty() {
|
||||||
source.architectures.push(local_arch.clone());
|
source.architectures.push(local_arch.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure all components are present
|
// Ensure all components are present
|
||||||
let required_components = ["main", "restricted", "universe", "multiverse"];
|
for comp in &components {
|
||||||
for &comp in &required_components {
|
if !source.components.contains(comp) {
|
||||||
if !source.components.contains(&comp.to_string()) {
|
source.components.push(comp.clone());
|
||||||
source.components.push(comp.to_string());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure all suites (pockets) are enabled, excluding 'proposed'
|
// Ensure all suites (pockets) are enabled
|
||||||
// unless explicitly requested through the 'pocket' option
|
for suite in &required_suites {
|
||||||
let mut required_suites = vec![
|
if !source.suite.contains(suite) {
|
||||||
series.to_string(),
|
source.suite.push(suite.clone());
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for suite in required_suites {
|
|
||||||
if !source.suite.contains(&suite) {
|
|
||||||
source.suite.push(suite);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if ports repository already exists for the target architecture
|
// Check whether an existing source already serves the target
|
||||||
let has_ports = sources
|
// architecture from its mirror (e.g. the ports mirror for a
|
||||||
.iter()
|
// non-local arch); when cross-building for the local architecture,
|
||||||
.any(|s| s.uri.contains("ports.ubuntu.com") && s.architectures.contains(&arch.to_string()));
|
// 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 {
|
if !has_target {
|
||||||
// Add ports repository for the target architecture
|
// Add the target architecture's mirror (ports for the non-local
|
||||||
let mut ports_suites = vec![
|
// architectures on Ubuntu)
|
||||||
series.to_string(),
|
let mirror_entry = crate::apt::sources::SourceEntry {
|
||||||
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 {
|
|
||||||
enabled: true,
|
enabled: true,
|
||||||
kind: crate::apt::sources::SourceKind::Deb,
|
kind: crate::apt::sources::SourceKind::Deb,
|
||||||
components: vec![
|
components: components.clone(),
|
||||||
"main".to_string(),
|
|
||||||
"restricted".to_string(),
|
|
||||||
"universe".to_string(),
|
|
||||||
"multiverse".to_string(),
|
|
||||||
],
|
|
||||||
architectures: vec![arch.to_string()],
|
architectures: vec![arch.to_string()],
|
||||||
uri: "http://ports.ubuntu.com/ubuntu-ports".to_string(),
|
uri: target_mirror.url.clone(),
|
||||||
signed_by: None,
|
signed_by: None,
|
||||||
trusted: None,
|
trusted: None,
|
||||||
suite: ports_suites,
|
suite: required_suites.clone(),
|
||||||
// No origin: saved to the pkh-owned added-sources file
|
// No origin: saved to the pkh-owned added-sources file
|
||||||
origin: None,
|
origin: None,
|
||||||
};
|
};
|
||||||
sources.push(ports_entry);
|
sources.push(mirror_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the updated sources: each entry is written back to its origin
|
// 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),
|
// 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)?;
|
crate::apt::sources::save(Some(ctx.clone()), sources)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -234,4 +255,38 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(env.len(), 2);
|
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