diff --git a/src/apt/sources.rs b/src/apt/sources.rs index 003619d..a022b5d 100644 --- a/src/apt/sources.rs +++ b/src/apt/sources.rs @@ -16,8 +16,8 @@ pub struct SourceEntry { pub architectures: Vec, /// Source URI pub uri: String, - /// Source suite (series-pocket) - pub suite: String, + /// Source suites (series-pocket) + pub suite: Vec, } impl SourceEntry { @@ -28,7 +28,7 @@ impl SourceEntry { components: Vec::new(), architectures: Vec::new(), uri: String::new(), - suite: String::new(), + suite: Vec::new(), }; for line in data.lines() { @@ -55,7 +55,10 @@ impl SourceEntry { // We only care about deb types } "URIs" => current_entry.uri = value.to_string(), - "Suites" => current_entry.suite = value.to_string(), + "Suites" => { + current_entry.suite = + value.split_whitespace().map(|s| s.to_string()).collect(); + } "Components" => { current_entry.components = value.split_whitespace().map(|s| s.to_string()).collect(); @@ -123,7 +126,7 @@ impl SourceEntry { } let uri = parts[1].to_string(); - let suite = parts[2].to_string(); + let suite = vec![parts[2].to_string()]; let components: Vec = parts[3..].iter().map(|&s| s.to_string()).collect(); Some(SourceEntry { @@ -139,26 +142,31 @@ impl SourceEntry { pub fn to_legacy(&self) -> String { let mut result = String::new(); - // Start with "deb" type - result.push_str("deb"); + // Legacy entries contain one suite per line + for suite in &self.suite { + // Start with "deb" type + result.push_str("deb"); - // Add architectures if present - if !self.architectures.is_empty() { - result.push_str(" [arch="); - result.push_str(&self.architectures.join(",")); - result.push(']'); - } + // Add architectures if present + if !self.architectures.is_empty() { + result.push_str(" [arch="); + result.push_str(&self.architectures.join(",")); + result.push(']'); + } - // Add URI and suite - result.push(' '); - result.push_str(&self.uri); - result.push(' '); - result.push_str(&self.suite); - - // Add components - if !self.components.is_empty() { + // Add URI and suite result.push(' '); - result.push_str(&self.components.join(" ")); + result.push_str(&self.uri); + result.push(' '); + result.push_str(suite); + + // Add components + if !self.components.is_empty() { + result.push(' '); + result.push_str(&self.components.join(" ")); + } + + result.push('\n'); } result @@ -275,12 +283,17 @@ mod tests { assert_eq!(sources.len(), 3); assert_eq!(sources[0].uri, "http://fr.archive.ubuntu.com/ubuntu/"); assert_eq!(sources[0].architectures, vec!["amd64"]); + assert_eq!( + sources[0].suite, + vec!["questing", "questing-updates", "questing-backports"] + ); assert_eq!( sources[0].components, vec!["main", "restricted", "universe", "multiverse"] ); assert_eq!(sources[1].uri, "http://security.ubuntu.com/ubuntu/"); assert_eq!(sources[1].architectures, vec!["amd64"]); + assert_eq!(sources[1].suite, vec!["questing-security"]); assert_eq!( sources[1].components, vec!["main", "restricted", "universe", "multiverse"] @@ -288,6 +301,10 @@ mod tests { assert_eq!(sources[2].uri, "http://ports.ubuntu.com/ubuntu-ports/"); assert_eq!(sources[2].architectures.len(), 1); assert_eq!(sources[2].architectures, vec!["riscv64"]); + assert_eq!( + sources[2].suite, + vec!["questing", "questing-updates", "questing-backports"] + ); assert_eq!( sources[2].components, vec!["main", "restricted", "universe", "multiverse"] @@ -305,15 +322,15 @@ mod tests { let sources = parse_legacy(legacy); assert_eq!(sources.len(), 3); assert_eq!(sources[0].uri, "http://archive.ubuntu.com/ubuntu"); - assert_eq!(sources[0].suite, "resolute"); + assert_eq!(sources[0].suite, vec!["resolute"]); assert_eq!(sources[0].components, vec!["main", "universe"]); assert_eq!(sources[0].architectures, vec!["amd64"]); assert_eq!(sources[1].uri, "http://archive.ubuntu.com/ubuntu"); - assert_eq!(sources[1].suite, "resolute-updates"); + assert_eq!(sources[1].suite, vec!["resolute-updates"]); assert_eq!(sources[1].components, vec!["main"]); assert_eq!(sources[1].architectures, vec!["amd64", "i386"]); assert_eq!(sources[2].uri, "http://security.ubuntu.com/ubuntu"); - assert_eq!(sources[2].suite, "resolute-security"); + assert_eq!(sources[2].suite, vec!["resolute-security"]); assert_eq!(sources[2].components, vec!["main"]); } } diff --git a/src/deb/cross.rs b/src/deb/cross.rs index 2782ca7..b62249b 100644 --- a/src/deb/cross.rs +++ b/src/deb/cross.rs @@ -51,145 +51,99 @@ pub fn ensure_repositories(arch: &str, series: &str) -> Result<(), Box Result<(), Box> { - // Scope existing to local_arch if not already scoped - ctx.command("sed") - .arg("-i") - .arg(format!("/URIs:.*\\(archive\\|security\\)\\.ubuntu\\.com/ {{ n; /^Architectures:/ ! i Architectures: {} }}", local_arch)) - .arg(deb822_path) - .status()?; - - // Ensure all components are enabled for the primary architecture - ctx.command("sed") - .arg("-i") - .arg("/URIs:.*\\(archive\\|security\\)\\.ubuntu\\.com/,/Components:/ s/^Components:.*/Components: main restricted universe multiverse/") - .arg(deb822_path) - .status()?; - - // Ensure all suites (pockets) are enabled for the primary architecture - // Excluding 'proposed' as it contains unstable software - let suites = format!("{series} {series}-updates {series}-backports {series}-security"); - ctx.command("sed") - .arg("-i") - .arg(format!( - "/URIs:.*\\(archive\\|security\\)\\.ubuntu\\.com/,/Suites:/ s/^Suites:.*/Suites: {}/", - suites - )) - .arg(deb822_path) - .status()?; - - // Add ports if not already present - let has_ports = ctx - .command("grep") - .arg("-q") - .arg("ports.ubuntu.com") - .arg(deb822_path) - .status()? - .success(); - - if !has_ports { - let ports_block = format!( - "\nTypes: deb\nURIs: http://ports.ubuntu.com/ubuntu-ports\nSuites: {series} {series}-updates {series}-backports {series}-security\nComponents: main restricted universe multiverse\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\nArchitectures: {arch}\n" - ); - ctx.command("sh") - .arg("-c") - .arg(format!("echo '{}' >> {}", ports_block, deb822_path)) - .status()?; - } - Ok(()) -} - -fn ensure_repositories_legacy( - ctx: &context::Context, - arch: &str, - local_arch: &str, - series: &str, - sources_path: &str, -) -> Result<(), Box> { - // Scope archive.ubuntu.com and security.ubuntu.com to local_arch if not already scoped - ctx.command("sed") - .arg("-i") - .arg(format!( - r"/archive.ubuntu.com\|security.ubuntu.com/ {{ /arch=/ ! {{ /^deb \[/ ! s/^deb /deb [arch={}] /; /^deb \[/ s/^deb \[\([^]]*\)\]/deb [arch={} \1]/ }} }}", - local_arch, local_arch - )) - .arg(sources_path) - .status()?; - - // Ensure all components (main restricted universe multiverse) are present for all archive/security lines - ctx.command("sed") - .arg("-i") - .arg(r"/archive.ubuntu.com\|security.ubuntu.com/ s/\( main\)\?\([ ]\+restricted\)\?\([ ]\+universe\)\?\([ ]\+multiverse\)\?$/ main restricted universe multiverse/") - .arg(sources_path) - .status()?; - - // Ensure all pockets exist. If not, we append them. - for pocket in ["", "-updates", "-backports", "-security"] { - let suite = format!("{}{}", series, pocket); - let has_suite = ctx - .command("grep") - .arg("-q") - .arg(format!(" {}", suite)) - .arg(sources_path) - .status()? - .success(); - - if !has_suite { - let line = format!( - "deb [arch={}] http://archive.ubuntu.com/ubuntu/ {} main restricted universe multiverse", - local_arch, suite - ); - ctx.command("sh") - .arg("-c") - .arg(format!("echo '{}' >> {}", line, sources_path)) - .status()?; + .success() + { + // For DEB822 format, we need to reconstruct the file content + let mut content = String::new(); + for source in &sources { + if !source.enabled { + continue; + } + content.push_str("Types: deb\n"); + content.push_str(&format!("URIs: {}\n", source.uri)); + content.push_str(&format!("Suites: {}\n", source.suite.join(" "))); + content.push_str(&format!("Components: {}\n", source.components.join(" "))); + content.push_str("Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\n"); + content.push_str(&format!( + "Architectures: {}\n", + source.architectures.join(" ") + )); + content.push('\n'); } + ctx.write_file(std::path::Path::new(deb822_path), &content)?; + } else { + // Fall back to legacy format + crate::apt::sources::save_legacy(Some(ctx.clone()), sources, "/etc/apt/sources.list")?; } - // Add ports repository to sources.list if not already present - let has_ports = ctx - .command("grep") - .arg("-q") - .arg("ports.ubuntu.com") - .arg(sources_path) - .status()? - .success(); - - if !has_ports { - let ports_lines = format!( - "deb [arch={arch}] http://ports.ubuntu.com/ubuntu-ports {series} main restricted universe multiverse\n\ - deb [arch={arch}] http://ports.ubuntu.com/ubuntu-ports {series}-updates main restricted universe multiverse\n\ - deb [arch={arch}] http://ports.ubuntu.com/ubuntu-ports {series}-backports main restricted universe multiverse\n\ - deb [arch={arch}] http://ports.ubuntu.com/ubuntu-ports {series}-security main restricted universe multiverse" - ); - ctx.command("sh") - .arg("-c") - .arg(format!("echo '{}' >> {}", ports_lines, sources_path)) - .status()?; - } Ok(()) }