distro_info: match changelog suite names with their series

Debian packages conventionally target 'unstable' in their
debian/changelog distribution field, but the series data (the
distro-info CSVs) only knows codenames: the suite is the alias
'unstable' of the series 'sid', a mapping the debian-distro-info tool
resolves internally without exposing it in its data.

Add a per-dist suite_aliases reference-data key (debian: unstable ->
sid), with two helpers on top: resolve_suite_alias, identifying a
changelog suite name with its series codename and the dist that
codename belongs to, and series_suite_alias, the inverse direction.
The two names identify the same series.
This commit is contained in:
2026-09-21 11:52:39 +02:00
parent 37e0b5c978
commit 8c6f6f4028
2 changed files with 74 additions and 0 deletions
+10
View File
@@ -28,6 +28,12 @@
## series (`<series>-updates`, ...). Deliberately not the
## `pockets` key: that one is the *search order* of pull,
## where backports must not fold in.
## suite_aliases: the changelog suite names that alias a series
## codename: Debian packages conventionally target
## 'unstable' where the series data carries 'sid'.
## Mapped suite name -> series codename; the two
## names identify the same series, and the selector
## offers the aliased entry as '<suite> (<series>)'.
## build_profiles: the vendor's default DEB_BUILD_PROFILES (Ubuntu
## activates derivative.ubuntu noudeb, Debian none),
## mirroring what Dpkg::BuildProfiles resolves when the
@@ -47,6 +53,10 @@ dist:
- updates
- security
- proposed-updates
# Debian changelogs conventionally target 'unstable'; the series data
# knows the same series as 'sid'.
suite_aliases:
unstable: sid
sections:
# Valid Section values for debian/control: the Debian policy section
# list unioned with the sections observed in the live Ubuntu archive.
+64
View File
@@ -87,6 +87,11 @@ struct DistData {
cross_pockets: Vec<String>,
#[serde(default)]
build_profiles: Vec<String>,
/// Changelog suite names aliasing a distro-info series codename
/// ('unstable' for Debian's 'sid'): the two names identify the same
/// series ([`series_suite_alias`], [`resolve_suite_alias`])
#[serde(default)]
suite_aliases: HashMap<String, String>,
series: SeriesInfo,
}
@@ -413,6 +418,30 @@ pub async fn get_dist_from_series(series: &str) -> Result<String, Box<dyn Error>
Err(format!("Unknown series: {}", series).into())
}
/// The changelog suite name that aliases the series codename of `dist`
/// (Debian's 'unstable' for 'sid'): the two names identify the same
/// series. `None` when the series carries no suite alias.
pub fn series_suite_alias(dist: &str, series: &str) -> Option<String> {
dist_data(dist)
.ok()?
.suite_aliases
.iter()
.find(|(_suite, codename)| codename.as_str() == series)
.map(|(suite, _)| suite.clone())
}
/// Identify a changelog suite name with the distro-info series codename
/// it aliases (Debian's 'unstable' is 'sid'), and the dist that codename
/// belongs to. `None` when `suite` is not a known alias of any dist.
pub fn resolve_suite_alias(suite: &str) -> Option<(String, String)> {
for (dist, data) in DATA.dist.iter() {
if let Some(codename) = data.suite_aliases.get(suite) {
return Some((dist.clone(), codename.clone()));
}
}
None
}
/// Get the package pockets available for a given distribution, in search order
///
/// The main archive ('') comes first so that a search without an explicit
@@ -1035,6 +1064,41 @@ mod tests {
assert!(series.contains(&"jammy".to_string()));
}
/// Suite aliases identify a changelog suite name with the series
/// codename of the same series: Debian's 'unstable' is 'sid'
#[test]
fn test_suite_aliases() {
assert_eq!(
resolve_suite_alias("unstable"),
Some(("debian".to_string(), "sid".to_string()))
);
// A series codename or unknown suite is not an alias
assert_eq!(resolve_suite_alias("sid"), None);
assert_eq!(resolve_suite_alias("noble"), None);
assert_eq!(
series_suite_alias("debian", "sid"),
Some("unstable".to_string())
);
assert_eq!(series_suite_alias("debian", "trixie"), None);
assert_eq!(series_suite_alias("ubuntu", "noble"), None);
}
/// Every suite alias must map to a real series of its dist, or the
/// selector would offer a phantom entry
#[tokio::test]
async fn test_suite_aliases_target_real_series() {
for (dist, data) in DATA.dist.iter() {
for (suite, codename) in &data.suite_aliases {
let series = get_ordered_series_name(dist).await.unwrap_or_default();
assert!(
series.contains(codename),
"suite alias '{suite}' of {dist} maps to '{codename}', \
which is not a known series"
);
}
}
}
#[tokio::test]
async fn test_get_dist_from_series() {
assert_eq!(get_dist_from_series("sid").await.unwrap(), "debian");