build,debian: extract reusable Debian format primitives from build/

Move the generic components out of src/build/ into a new src/debian/
module so they can be reused independently of the build pipeline:
deb822 control parsing (plus the debian/control model), file checksum
registry, debian/files registry, Debian version handling and changelog
entry parsing.

Merge OpenPGP clearsigning into utils/gpg.rs next to the existing key
discovery helper, making signing available outside of builds.

Delegate changelog.rs header/footer parsing to the new
debian::changelog parser, removing the duplicate regex implementation.

src/build/ keeps only build-specific logic: the pipeline driver,
build types, environment setup and the .buildinfo/.changes writers.
This commit is contained in:
2026-08-23 01:43:41 +02:00
parent 9d2519ed7b
commit c2e1288bc5
13 changed files with 346 additions and 392 deletions
+5 -74
View File
@@ -2,7 +2,7 @@ use chrono::Local;
use git2::{Oid, Repository, Sort};
use regex::Regex;
use std::fs::File;
use std::io::{self, BufRead, Read, Write};
use std::io::{Read, Write};
use std::path::Path;
/// Automatically generate a changelog entry from a commit history and previous changelog
@@ -121,84 +121,15 @@ fn increment_suffix(version: &str, suffix: &str) -> String {
pub fn parse_changelog_header(
path: &Path,
) -> Result<(String, String, String), Box<dyn std::error::Error>> {
let file = File::open(path).map_err(|e| {
format!(
"Failed to read changelog '{}': {}. \
Make sure you are running this command from the root of a source package \
(a directory containing a 'debian/' subdirectory with a 'changelog' file).",
path.display(),
e
)
})?;
let mut reader = io::BufReader::new(file);
let mut first_line = String::new();
reader.read_line(&mut first_line).map_err(|e| {
format!(
"Failed to read first line of changelog '{}': {}",
path.display(),
e
)
})?;
// Format: package (version) series; urgency=urgency
let re = Regex::new(r"^(\S+) \(([^)]+)\) (.*); .*")?;
if let Some(caps) = re.captures(&first_line) {
let package = caps.get(1).map_or("", |m| m.as_str()).to_string();
let version = caps.get(2).map_or("", |m| m.as_str()).to_string();
let series = caps.get(3).map_or("", |m| m.as_str()).to_string();
Ok((package, version, series))
} else {
Err(format!(
"Invalid changelog header format in '{}'. \
The first line must look like: `package (version) series; urgency=...`, \
but got: {:?}",
path.display(),
first_line.trim_end()
)
.into())
}
let entry = crate::debian::parse_changelog_entry(path)?;
Ok((entry.source, entry.version.full(), entry.distribution))
}
/// Parse a changelog file footer to extract maintainer information
/// Returns (name, email) tuple from the last modification entry
pub fn parse_changelog_footer(path: &Path) -> Result<(String, String), Box<dyn std::error::Error>> {
let mut file = File::open(path).map_err(|e| {
format!(
"Failed to read changelog '{}': {}. \
Make sure you are running this command from the root of a source package \
(a directory containing a 'debian/' subdirectory with a 'changelog' file).",
path.display(),
e
)
})?;
let mut content = String::new();
file.read_to_string(&mut content)
.map_err(|e| format!("Failed to read changelog '{}': {}", path.display(), e))?;
// Find the last maintainer line (format: -- Name <email> Date)
let re = Regex::new(r"--\s*([^<]+?)\s*<([^>]+)>\s*")?;
if let Some(first_match) = re.captures_iter(&content).next() {
let name = first_match
.get(1)
.map_or("", |m| m.as_str())
.trim()
.to_string();
let email = first_match
.get(2)
.map_or("", |m| m.as_str())
.trim()
.to_string();
Ok((name, email))
} else {
Err(format!(
"No maintainer information found in '{}'. \
The changelog must contain a line of the form '-- Name <email> Date', \
but none was found.",
path.display()
)
.into())
}
let entry = crate::debian::parse_changelog_entry(path)?;
Ok((entry.maintainer_name, entry.maintainer_email))
}
/*