Handle malformed remote and edge-case data instead of panicking
- distro_info: malformed CSV rows are skipped with a warning, dates that fail to parse become None, and all plain HTTP requests go through a shared reqwest client with connect/total timeouts - package_info: the Sources stanza iterator is iterative (a crafted index with many blank stanzas overflowed the stack), stanzas missing a Version are skipped, and failed series/pocket probes are summarized in the final 'not found' error instead of being silently dropped - pull: no double unwrap on the remote-derived artifact filename, an empty series list is an error, and streaming downloads get a per-request timeout - deb/cross: dpkg-architecture output parsing skips unexpected lines and its exit status is checked, as is dpkg --add-architecture - changelog: version increments parse as u64 with checked arithmetic (1.0-20250123123456 used to panic on the u32 parse)
This commit is contained in:
+110
-17
@@ -1,24 +1,19 @@
|
||||
use crate::context::Context;
|
||||
use log::debug;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Set environment variables for cross-compilation
|
||||
pub fn setup_environment(
|
||||
env: &mut HashMap<String, String>,
|
||||
arch: &str,
|
||||
ctx: Arc<Context>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let dpkg_architecture = String::from_utf8(
|
||||
ctx.command("dpkg-architecture")
|
||||
.arg("-a")
|
||||
.arg(arch)
|
||||
.output()?
|
||||
.stdout,
|
||||
)?;
|
||||
/// Parse 'dpkg-architecture' output (KEY=value lines) into a set of
|
||||
/// environment variables. Unexpected lines (e.g. warnings on stderr leaking
|
||||
/// into stdout) are skipped instead of causing a failure.
|
||||
fn parse_dpkg_architecture_output(output: &str, env: &mut HashMap<String, String>) {
|
||||
let env_var_regex = regex::Regex::new(r"(?<key>.*)=(?<value>.*)").unwrap();
|
||||
for l in dpkg_architecture.lines() {
|
||||
let capture = env_var_regex.captures(l).unwrap();
|
||||
for l in output.lines() {
|
||||
let Some(capture) = env_var_regex.captures(l) else {
|
||||
debug!("Skipping unexpected dpkg-architecture output line: '{l}'");
|
||||
continue;
|
||||
};
|
||||
let key = capture.name("key").unwrap().as_str().to_string();
|
||||
let value = capture.name("value").unwrap().as_str().to_string();
|
||||
|
||||
@@ -28,6 +23,45 @@ pub fn setup_environment(
|
||||
env.insert("CROSS_COMPILE".to_string(), format!("{value}-"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set environment variables for cross-compilation
|
||||
pub fn setup_environment(
|
||||
env: &mut HashMap<String, String>,
|
||||
arch: &str,
|
||||
ctx: Arc<Context>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let output = ctx
|
||||
.command("dpkg-architecture")
|
||||
.arg("-a")
|
||||
.arg(arch)
|
||||
.output()
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Failed to run 'dpkg-architecture -a {arch}': {e}. \
|
||||
Is 'dpkg-dev' installed?"
|
||||
)
|
||||
})?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(format!(
|
||||
"'dpkg-architecture -a {}' failed with status: {}.{}",
|
||||
arch,
|
||||
output.status,
|
||||
if stderr.trim().is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("\ndpkg-architecture output:\n{}", stderr.trim())
|
||||
}
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let dpkg_architecture = String::from_utf8(output.stdout)
|
||||
.map_err(|e| format!("Invalid UTF-8 in dpkg-architecture output: {e}"))?;
|
||||
parse_dpkg_architecture_output(&dpkg_architecture, env);
|
||||
|
||||
env.insert("DEB_BUILD_PROFILES".to_string(), "cross".to_string());
|
||||
|
||||
Ok(())
|
||||
@@ -44,10 +78,19 @@ pub fn ensure_repositories(
|
||||
let local_arch = crate::get_current_arch();
|
||||
|
||||
// Add target ('host') architecture
|
||||
ctx.command("dpkg")
|
||||
let status = ctx
|
||||
.command("dpkg")
|
||||
.arg("--add-architecture")
|
||||
.arg(arch)
|
||||
.status()?;
|
||||
.status()
|
||||
.map_err(|e| format!("Failed to run 'dpkg --add-architecture {arch}': {e}"))?;
|
||||
if !status.success() {
|
||||
return Err(format!(
|
||||
"'dpkg --add-architecture {}' failed with status: {}",
|
||||
arch, status
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
// Check if we are on Ubuntu
|
||||
let os_release = String::from_utf8(ctx.command("cat").arg("/etc/os-release").output()?.stdout)?;
|
||||
@@ -142,3 +185,53 @@ pub fn ensure_repositories(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_dpkg_architecture_output() {
|
||||
let output = "DEB_BUILD_ARCH=amd64\n\
|
||||
DEB_HOST_ARCH=arm64\n\
|
||||
DEB_HOST_GNU_TYPE=aarch64-linux-gnu\n";
|
||||
|
||||
let mut env = HashMap::new();
|
||||
parse_dpkg_architecture_output(output, &mut env);
|
||||
|
||||
assert_eq!(env.get("DEB_BUILD_ARCH").map(String::as_str), Some("amd64"));
|
||||
assert_eq!(env.get("DEB_HOST_ARCH").map(String::as_str), Some("arm64"));
|
||||
assert_eq!(
|
||||
env.get("DEB_HOST_GNU_TYPE").map(String::as_str),
|
||||
Some("aarch64-linux-gnu")
|
||||
);
|
||||
// Derived variable for the GNU type
|
||||
assert_eq!(
|
||||
env.get("CROSS_COMPILE").map(String::as_str),
|
||||
Some("aarch64-linux-gnu-")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_dpkg_architecture_output_skips_unexpected_lines() {
|
||||
// Unexpected lines (warnings on stdout, empty lines) must be skipped
|
||||
// instead of panicking
|
||||
let output = "dpkg-architecture: warning: something odd happened\n\
|
||||
\n\
|
||||
DEB_HOST_GNU_TYPE=arm-linux-gnueabihf\n\
|
||||
not an environment variable assignment\n";
|
||||
|
||||
let mut env = HashMap::new();
|
||||
parse_dpkg_architecture_output(output, &mut env);
|
||||
|
||||
assert_eq!(
|
||||
env.get("DEB_HOST_GNU_TYPE").map(String::as_str),
|
||||
Some("arm-linux-gnueabihf")
|
||||
);
|
||||
assert_eq!(
|
||||
env.get("CROSS_COMPILE").map(String::as_str),
|
||||
Some("arm-linux-gnueabihf-")
|
||||
);
|
||||
assert_eq!(env.len(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user