Compare commits
3
Commits
47bb7c608e
...
4f5246ccd3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f5246ccd3 | ||
|
|
dd2438a72c | ||
|
|
012df20961 |
@@ -90,7 +90,7 @@ Missing features:
|
||||
- [ ] Try to fetch the correct git branch for series on Debian, or fallback to the archive
|
||||
- [ ] `pkh chlog`
|
||||
- [x] Auto-generate changelog entry
|
||||
- [ ] Extra flags: backport, non-maintainer upload, no change rebuild, ...
|
||||
- [x] Extra flags: backport, non-maintainer upload, no change rebuild, ...
|
||||
- [ ] Commit changelog entry
|
||||
- [ ] `pkh build`
|
||||
- [x] Build the source package
|
||||
|
||||
+372
-54
@@ -22,12 +22,44 @@ pub struct GeneratedEntry {
|
||||
pub path: std::path::PathBuf,
|
||||
}
|
||||
|
||||
/// The kind of upload a generated changelog entry describes: it selects how
|
||||
/// the new entry's version is derived from the previous one. Ignored when an
|
||||
/// explicit version is given.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub enum EntryKind {
|
||||
/// A regular upload: the version numbering follows the vendor of the
|
||||
/// target series — the Debian convention (`1.0-1` becomes `1.0-2`) for
|
||||
/// Debian series, the Ubuntu one for Ubuntu series (`1.0-1` becomes
|
||||
/// `1.0-1ubuntu1`, an already-Ubuntu `1.0-1ubuntu1` becomes
|
||||
/// `1.0-1ubuntu2`). Unresolvable series (UNRELEASED, unknown) number
|
||||
/// the Debian way.
|
||||
#[default]
|
||||
Normal,
|
||||
/// A non-maintainer upload: `1.0-1` becomes `1.0-1.1` (native `1.0`
|
||||
/// becomes `1.0+nmu1`)
|
||||
Nmu,
|
||||
/// A no-change rebuild: `1.0-1` becomes `1.0-1build1`
|
||||
Rebuild,
|
||||
/// An Ubuntu upload regardless of the target series: `1.0-1` becomes
|
||||
/// `1.0-1ubuntu1`. Library-only: the CLI has no flag selecting it,
|
||||
/// and targeting an Ubuntu series already picks this numbering.
|
||||
Ubuntu,
|
||||
/// A backport: numbered after the vendor of the target series —
|
||||
/// Debian's backports scheme (`1.0-1` becomes `1.0-1~bpo12+1`) or
|
||||
/// Ubuntu's per-release SRU scheme (`3.1-1ubuntu2` becomes
|
||||
/// `3.1-1ubuntu2~24.04.1`). The release number is derived from the
|
||||
/// target series, which must therefore be a numbered release (e.g.
|
||||
/// not sid).
|
||||
Backport,
|
||||
}
|
||||
|
||||
/// Automatically generate a changelog entry from a commit history and previous changelog
|
||||
pub fn generate_entry(
|
||||
pub async fn generate_entry(
|
||||
changelog_file: &str,
|
||||
cwd: Option<&Path>,
|
||||
user_version: Option<&str>,
|
||||
target_series: Option<&str>,
|
||||
kind: EntryKind,
|
||||
) -> Result<GeneratedEntry, Box<dyn std::error::Error>> {
|
||||
let changelog_path = if let Some(path) = cwd {
|
||||
path.join(changelog_file)
|
||||
@@ -51,16 +83,30 @@ pub fn generate_entry(
|
||||
Err(_e) => Vec::new(),
|
||||
};
|
||||
|
||||
// The series the new entry targets: needed before the version is
|
||||
// computed, because a backport version carries the target release number
|
||||
let series = target_series.unwrap_or(¤t_series).to_string();
|
||||
|
||||
// Compute new version if needed, or use user-supplied one
|
||||
let new_version = if let Some(version) = user_version {
|
||||
version.to_string()
|
||||
} else {
|
||||
// TODO: Pass these flags from CLI
|
||||
compute_new_version(&old_version, false, false, false)?
|
||||
match kind {
|
||||
EntryKind::Normal => {
|
||||
let bump = normal_bump_for_series(&series).await;
|
||||
compute_new_version(&old_version, bump)?
|
||||
}
|
||||
EntryKind::Nmu => compute_new_version(&old_version, Bump::Nmu)?,
|
||||
EntryKind::Rebuild => compute_new_version(&old_version, Bump::Rebuild)?,
|
||||
EntryKind::Ubuntu => compute_new_version(&old_version, Bump::Ubuntu)?,
|
||||
EntryKind::Backport => {
|
||||
let suffix = backport_suffix_for_series(&series).await?;
|
||||
compute_new_version(&old_version, Bump::Backport(suffix))?
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (maintainer_name, maintainer_email) = get_maintainer_info()?;
|
||||
let series = target_series.unwrap_or(¤t_series).to_string();
|
||||
let new_entry = format_entry(
|
||||
&package,
|
||||
&new_version,
|
||||
@@ -81,28 +127,106 @@ pub fn generate_entry(
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute the next (most probable) version number of a package, from old version and
|
||||
/// conditions on changes (is ubuntu upload, is a no change rebuild, is a non-maintainer upload)
|
||||
/// How the new version is derived from the previous one: the conditions
|
||||
/// [`compute_new_version`] acts on. The suffixes follow the usual
|
||||
/// Debian/Ubuntu numbering conventions.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum Bump {
|
||||
/// Regular upload: increment the trailing number
|
||||
Normal,
|
||||
/// Ubuntu upload: `1.0-9` becomes `1.0-9ubuntu1`
|
||||
Ubuntu,
|
||||
/// Non-maintainer upload: `1.0-1` becomes `1.0-1.1`, native `1.0`
|
||||
/// becomes `1.0+nmu1`
|
||||
Nmu,
|
||||
/// No-change rebuild: `1.0-1` becomes `1.0-1build1`
|
||||
Rebuild,
|
||||
/// Backport to the release whose version suffix is carried
|
||||
/// (`"~bpo12+"` for Debian bookworm, `"~24.04."` for Ubuntu noble):
|
||||
/// `1.0-1` becomes `1.0-1~bpo12+1`, `3.1-1ubuntu2` becomes
|
||||
/// `3.1-1ubuntu2~24.04.1`
|
||||
Backport(String),
|
||||
}
|
||||
|
||||
/// Compute the next (most probable) version number of a package, from the
|
||||
/// old version and the kind of upload the entry describes
|
||||
fn compute_new_version(
|
||||
old_version: &str,
|
||||
is_ubuntu: bool,
|
||||
is_rebuild: bool,
|
||||
is_nmu: bool,
|
||||
bump: Bump,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
if is_ubuntu {
|
||||
return increment_suffix(old_version, "ubuntu");
|
||||
}
|
||||
if is_rebuild {
|
||||
return increment_suffix(old_version, "build");
|
||||
}
|
||||
if is_nmu {
|
||||
if !old_version.contains('-') {
|
||||
return increment_suffix(old_version, "+nmu");
|
||||
} else {
|
||||
return increment_suffix(old_version, ".");
|
||||
match bump {
|
||||
Bump::Ubuntu => increment_suffix(old_version, "ubuntu"),
|
||||
Bump::Rebuild => increment_suffix(old_version, "build"),
|
||||
Bump::Nmu => {
|
||||
if old_version.contains('-') {
|
||||
increment_suffix(old_version, ".")
|
||||
} else {
|
||||
increment_suffix(old_version, "+nmu")
|
||||
}
|
||||
}
|
||||
// A re-backport of the same release reuses its suffix counter,
|
||||
// incrementing the trailing number: increment_suffix appends the
|
||||
// suffix with a fresh 1 when the version carries no such suffix
|
||||
// yet (including when it is a backport of another release, whose
|
||||
// counter is left untouched)
|
||||
Bump::Backport(suffix) => increment_suffix(old_version, &suffix),
|
||||
Bump::Normal => increment_suffix(old_version, ""),
|
||||
}
|
||||
}
|
||||
|
||||
/// The version bump a regular ([`EntryKind::Normal`]) upload gets, derived
|
||||
/// from the vendor of the target series: Ubuntu series number their uploads
|
||||
/// the Ubuntu way (`1.0-1` becomes `1.0-1ubuntu1`), everything else —
|
||||
/// Debian series, but also UNRELEASED and series no distro-info data knows —
|
||||
/// numbers the Debian way (`1.0-1` becomes `1.0-2`).
|
||||
async fn normal_bump_for_series(series: &str) -> Bump {
|
||||
match crate::distro_info::get_dist_from_series(series).await {
|
||||
Ok(dist) if dist == "ubuntu" => Bump::Ubuntu,
|
||||
_ => Bump::Normal,
|
||||
}
|
||||
}
|
||||
|
||||
/// The version suffix that backport versions are numbered with, derived
|
||||
/// from the vendor of the target series: Debian backports follow the
|
||||
/// backports.debian.org scheme (`~bpo12+1`), Ubuntu backports the
|
||||
/// per-release SRU scheme of the Ubuntu version-strings documentation
|
||||
/// (`3.1-1ubuntu2~22.04.1` for a development version backported to 22.04).
|
||||
/// A backport suite name (`bookworm-backports`) is accepted too. Errors
|
||||
/// when the series has no usable release number (Debian sid or
|
||||
/// experimental, unknown series): the numbering cannot be derived for it.
|
||||
async fn backport_suffix_for_series(series: &str) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let base = series
|
||||
.strip_suffix("-backports-sloppy")
|
||||
.or_else(|| series.strip_suffix("-backports"))
|
||||
.unwrap_or(series);
|
||||
let unnumbered = || {
|
||||
format!(
|
||||
"Could not determine the release number of series '{series}', \
|
||||
needed to number the backport version (Debian ~bpo12+1, \
|
||||
Ubuntu ~24.04.1). Target a released Debian or Ubuntu series \
|
||||
or pass the version explicitly with --version."
|
||||
)
|
||||
};
|
||||
let Some((dist, number)) = crate::distro_info::get_series_release_number(base).await? else {
|
||||
return Err(unnumbered().into());
|
||||
};
|
||||
match dist.as_str() {
|
||||
// backports.debian.org numbers with the plain integer release number
|
||||
"debian" if !number.is_empty() && number.chars().all(|c| c.is_ascii_digit()) => {
|
||||
Ok(format!("~bpo{}+", number))
|
||||
}
|
||||
// Ubuntu numbers per release as YY.MM; the trailing `.N` counts
|
||||
// the per-release SRU uploads
|
||||
"ubuntu"
|
||||
if number.split('.').count() == 2
|
||||
&& number
|
||||
.split('.')
|
||||
.all(|part| !part.is_empty() && part.chars().all(|c| c.is_ascii_digit())) =>
|
||||
{
|
||||
Ok(format!("~{}.", number))
|
||||
}
|
||||
_ => Err(unnumbered().into()),
|
||||
}
|
||||
increment_suffix(old_version, "")
|
||||
}
|
||||
|
||||
/// Increment a version number by 1, for a given suffix
|
||||
@@ -473,6 +597,12 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// Serializes the tests that mutate the process-global DEBFULLNAME /
|
||||
/// DEBEMAIL variables: run in parallel, they otherwise race each
|
||||
/// other's identity reads and assertions.
|
||||
static IDENTITY_LOCK: std::sync::LazyLock<tokio::sync::Mutex<()>> =
|
||||
std::sync::LazyLock::new(|| tokio::sync::Mutex::new(()));
|
||||
|
||||
fn setup_repo(dir: &Path) {
|
||||
Command::new("git")
|
||||
.arg("init")
|
||||
@@ -515,8 +645,8 @@ mod tests {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_entry() {
|
||||
#[tokio::test]
|
||||
async fn test_generate_entry() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let repo_dir = temp_dir.path();
|
||||
setup_repo(repo_dir);
|
||||
@@ -542,11 +672,20 @@ mod tests {
|
||||
commit(repo_dir, "Add feature B");
|
||||
|
||||
// Generate entry
|
||||
let _identity = IDENTITY_LOCK.lock().await;
|
||||
unsafe {
|
||||
std::env::set_var("DEBFULLNAME", "Maintainer Maintainer");
|
||||
std::env::set_var("DEBEMAIL", "maintainer@maintainer.com");
|
||||
}
|
||||
generate_entry("debian/changelog", Some(repo_dir), None, None).unwrap();
|
||||
generate_entry(
|
||||
"debian/changelog",
|
||||
Some(repo_dir),
|
||||
None,
|
||||
None,
|
||||
EntryKind::Normal,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
unsafe {
|
||||
std::env::remove_var("DEBFULLNAME");
|
||||
std::env::remove_var("DEBEMAIL");
|
||||
@@ -568,78 +707,107 @@ mod tests {
|
||||
fn test_compute_new_version() {
|
||||
// Debian upload
|
||||
assert_eq!(
|
||||
compute_new_version("15.2.0-8", false, false, false).unwrap(),
|
||||
compute_new_version("15.2.0-8", Bump::Normal).unwrap(),
|
||||
"15.2.0-9"
|
||||
);
|
||||
assert_eq!(
|
||||
compute_new_version("15.2.0-9", false, false, false).unwrap(),
|
||||
compute_new_version("15.2.0-9", Bump::Normal).unwrap(),
|
||||
"15.2.0-10"
|
||||
);
|
||||
|
||||
// Ubuntu upload
|
||||
assert_eq!(
|
||||
compute_new_version("15.2.0-9", true, false, false).unwrap(),
|
||||
compute_new_version("15.2.0-9", Bump::Ubuntu).unwrap(),
|
||||
"15.2.0-9ubuntu1"
|
||||
);
|
||||
assert_eq!(
|
||||
compute_new_version("15.2.0-9ubuntu1", true, false, false).unwrap(),
|
||||
compute_new_version("15.2.0-9ubuntu1", Bump::Ubuntu).unwrap(),
|
||||
"15.2.0-9ubuntu2"
|
||||
);
|
||||
|
||||
// No change rebuild
|
||||
assert_eq!(
|
||||
compute_new_version("15.2.0-9", false, true, false).unwrap(),
|
||||
compute_new_version("15.2.0-9", Bump::Rebuild).unwrap(),
|
||||
"15.2.0-9build1"
|
||||
);
|
||||
assert_eq!(
|
||||
compute_new_version("15.2.0-9build1", false, true, false).unwrap(),
|
||||
compute_new_version("15.2.0-9build1", Bump::Rebuild).unwrap(),
|
||||
"15.2.0-9build2"
|
||||
);
|
||||
|
||||
// Rebuild of Ubuntu version
|
||||
assert_eq!(
|
||||
compute_new_version("15.2.0-9ubuntu1", false, true, false).unwrap(),
|
||||
compute_new_version("15.2.0-9ubuntu1", Bump::Rebuild).unwrap(),
|
||||
"15.2.0-9ubuntu1build1"
|
||||
);
|
||||
|
||||
// NMU
|
||||
// Native
|
||||
assert_eq!(compute_new_version("1.0", Bump::Nmu).unwrap(), "1.0+nmu1");
|
||||
assert_eq!(
|
||||
compute_new_version("1.0", false, false, true).unwrap(),
|
||||
"1.0+nmu1"
|
||||
);
|
||||
assert_eq!(
|
||||
compute_new_version("1.0+nmu1", false, false, true).unwrap(),
|
||||
compute_new_version("1.0+nmu1", Bump::Nmu).unwrap(),
|
||||
"1.0+nmu2"
|
||||
);
|
||||
|
||||
// Non-native
|
||||
assert_eq!(compute_new_version("1.0-1", Bump::Nmu).unwrap(), "1.0-1.1");
|
||||
assert_eq!(
|
||||
compute_new_version("1.0-1", false, false, true).unwrap(),
|
||||
"1.0-1.1"
|
||||
);
|
||||
assert_eq!(
|
||||
compute_new_version("1.0-1.1", false, false, true).unwrap(),
|
||||
compute_new_version("1.0-1.1", Bump::Nmu).unwrap(),
|
||||
"1.0-1.2"
|
||||
);
|
||||
|
||||
// NMU of NMU?
|
||||
assert_eq!(
|
||||
compute_new_version("1.0-1.2", false, false, true).unwrap(),
|
||||
compute_new_version("1.0-1.2", Bump::Nmu).unwrap(),
|
||||
"1.0-1.3"
|
||||
);
|
||||
|
||||
// Backport, Debian scheme
|
||||
assert_eq!(
|
||||
compute_new_version("1.0-1", Bump::Backport("~bpo12+".to_string())).unwrap(),
|
||||
"1.0-1~bpo12+1"
|
||||
);
|
||||
// Re-backporting the same source reuses the counter
|
||||
assert_eq!(
|
||||
compute_new_version("1.0-1~bpo12+1", Bump::Backport("~bpo12+".to_string())).unwrap(),
|
||||
"1.0-1~bpo12+2"
|
||||
);
|
||||
// Native packages backport too
|
||||
assert_eq!(
|
||||
compute_new_version("1.0", Bump::Backport("~bpo12+".to_string())).unwrap(),
|
||||
"1.0~bpo12+1"
|
||||
);
|
||||
// A version carrying another release's counter gains a fresh one
|
||||
assert_eq!(
|
||||
compute_new_version("1.0-1~bpo11+1", Bump::Backport("~bpo12+".to_string())).unwrap(),
|
||||
"1.0-1~bpo11+1~bpo12+1"
|
||||
);
|
||||
|
||||
// Backport, Ubuntu scheme (Ubuntu version-strings documentation)
|
||||
assert_eq!(
|
||||
compute_new_version("3.1-1ubuntu2", Bump::Backport("~22.04.".to_string())).unwrap(),
|
||||
"3.1-1ubuntu2~22.04.1"
|
||||
);
|
||||
// Subsequent per-release SRU uploads increment the counter
|
||||
assert_eq!(
|
||||
compute_new_version(
|
||||
"3.1-1ubuntu2~22.04.1",
|
||||
Bump::Backport("~22.04.".to_string())
|
||||
)
|
||||
.unwrap(),
|
||||
"3.1-1ubuntu2~22.04.2"
|
||||
);
|
||||
// Native packages
|
||||
assert_eq!(
|
||||
compute_new_version("3.1", Bump::Backport("~22.04.".to_string())).unwrap(),
|
||||
"3.1~22.04.1"
|
||||
);
|
||||
|
||||
// Native package uploads
|
||||
assert_eq!(compute_new_version("1.0", Bump::Normal).unwrap(), "1.1");
|
||||
assert_eq!(compute_new_version("1.0.5", Bump::Normal).unwrap(), "1.0.6");
|
||||
assert_eq!(
|
||||
compute_new_version("1.0", false, false, false).unwrap(),
|
||||
"1.1"
|
||||
);
|
||||
assert_eq!(
|
||||
compute_new_version("1.0.5", false, false, false).unwrap(),
|
||||
"1.0.6"
|
||||
);
|
||||
assert_eq!(
|
||||
compute_new_version("20241126", false, false, false).unwrap(),
|
||||
compute_new_version("20241126", Bump::Normal).unwrap(),
|
||||
"20241127"
|
||||
);
|
||||
}
|
||||
@@ -649,22 +817,172 @@ mod tests {
|
||||
// Date-based versions with a trailing number larger than u32::MAX
|
||||
// must increment normally (they fit in a u64)
|
||||
assert_eq!(
|
||||
compute_new_version("1.0-20250123123456", false, false, false).unwrap(),
|
||||
compute_new_version("1.0-20250123123456", Bump::Normal).unwrap(),
|
||||
"1.0-20250123123457"
|
||||
);
|
||||
|
||||
// A number that does not even fit in a u64 yields a clear error
|
||||
// instead of panicking
|
||||
let err = compute_new_version("1.0-99999999999999999999999999", false, false, false);
|
||||
let err = compute_new_version("1.0-99999999999999999999999999", Bump::Normal);
|
||||
assert!(err.is_err());
|
||||
|
||||
// u64::MAX itself cannot be incremented
|
||||
let err = compute_new_version("1.0-18446744073709551615", false, false, false);
|
||||
let err = compute_new_version("1.0-18446744073709551615", Bump::Normal);
|
||||
assert!(err.is_err());
|
||||
}
|
||||
|
||||
/// A backport entry is numbered after the Debian release number of the
|
||||
/// target series (`~bpo12+1` for bookworm). Relies on the host
|
||||
/// distro-info data mapping bookworm to 12 (see distro_info tests).
|
||||
#[tokio::test]
|
||||
async fn test_generate_entry_backport_numbering() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let repo_dir = temp_dir.path();
|
||||
let changelog_path = repo_dir.join("debian/changelog");
|
||||
std::fs::create_dir_all(repo_dir.join("debian")).unwrap();
|
||||
std::fs::write(
|
||||
&changelog_path,
|
||||
"mypackage (1.0-1) unstable; urgency=medium\n\n * Initial release\n\n -- Maintainer <m@e.com> Wed, 01 Jan 2020 00:00:00 +0000\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let _identity = IDENTITY_LOCK.lock().await;
|
||||
unsafe {
|
||||
std::env::set_var("DEBFULLNAME", "Maintainer Maintainer");
|
||||
std::env::set_var("DEBEMAIL", "maintainer@maintainer.com");
|
||||
}
|
||||
let entry = generate_entry(
|
||||
"debian/changelog",
|
||||
Some(repo_dir),
|
||||
None,
|
||||
Some("bookworm"),
|
||||
EntryKind::Backport,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
unsafe {
|
||||
std::env::remove_var("DEBFULLNAME");
|
||||
std::env::remove_var("DEBEMAIL");
|
||||
}
|
||||
|
||||
assert_eq!(entry.new_version, "1.0-1~bpo12+1");
|
||||
assert_eq!(entry.series, "bookworm");
|
||||
let content = std::fs::read_to_string(&changelog_path).unwrap();
|
||||
assert!(content.contains("mypackage (1.0-1~bpo12+1) bookworm; urgency=medium"));
|
||||
}
|
||||
|
||||
/// Backport numbering needs a usable release number: Debian's rolling
|
||||
/// series (sid) carry none and are rejected before anything is written,
|
||||
/// pointing at --series/--version instead.
|
||||
#[tokio::test]
|
||||
async fn test_generate_entry_backport_requires_numbered_series() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let repo_dir = temp_dir.path();
|
||||
let changelog_path = repo_dir.join("debian/changelog");
|
||||
std::fs::create_dir_all(repo_dir.join("debian")).unwrap();
|
||||
std::fs::write(
|
||||
&changelog_path,
|
||||
"mypackage (1.0-1) unstable; urgency=medium\n\n * Initial release\n\n -- Maintainer <m@e.com> Wed, 01 Jan 2020 00:00:00 +0000\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = generate_entry(
|
||||
"debian/changelog",
|
||||
Some(repo_dir),
|
||||
None,
|
||||
Some("sid"),
|
||||
EntryKind::Backport,
|
||||
)
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
|
||||
// Nothing was written: the changelog is untouched
|
||||
let content = std::fs::read_to_string(&changelog_path).unwrap();
|
||||
assert!(content.starts_with("mypackage (1.0-1) unstable"));
|
||||
}
|
||||
|
||||
/// Ubuntu backports follow the per-release SRU scheme of the Ubuntu
|
||||
/// version-strings documentation: the development release's version
|
||||
/// with ~YY.MM.1 appended, independently of what the target release
|
||||
/// carries. Relies on the host distro-info data mapping noble to
|
||||
/// 24.04 (see the distro_info tests).
|
||||
#[tokio::test]
|
||||
async fn test_generate_entry_backport_ubuntu_numbering() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let repo_dir = temp_dir.path();
|
||||
setup_repo(repo_dir);
|
||||
let changelog_path = repo_dir.join("debian/changelog");
|
||||
std::fs::create_dir_all(repo_dir.join("debian")).unwrap();
|
||||
std::fs::write(
|
||||
&changelog_path,
|
||||
"mypackage (3.1-1ubuntu2) questing; urgency=medium\n\n * Initial release\n\n -- Maintainer <m@e.com> Wed, 01 Jan 2020 00:00:00 +0000\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let entry = generate_entry(
|
||||
"debian/changelog",
|
||||
Some(repo_dir),
|
||||
None,
|
||||
Some("noble"),
|
||||
EntryKind::Backport,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(entry.new_version, "3.1-1ubuntu2~24.04.1");
|
||||
assert_eq!(entry.series, "noble");
|
||||
let content = std::fs::read_to_string(&changelog_path).unwrap();
|
||||
assert!(content.contains("mypackage (3.1-1ubuntu2~24.04.1) noble; urgency=medium"));
|
||||
}
|
||||
|
||||
/// A regular upload is numbered after the vendor of its target series:
|
||||
/// a Debian-style version uploaded to an Ubuntu series gains the
|
||||
/// ubuntu1 suffix, and re-bumping the now-Ubuntu changelog increments
|
||||
/// that counter instead of the revision. Relies on the host distro-info
|
||||
/// data listing noble (see the distro_info tests). The git repo
|
||||
/// provides the maintainer identity: DEBFULLNAME/DEBEMAIL are
|
||||
/// process-global and other tests mutate them in parallel.
|
||||
#[tokio::test]
|
||||
async fn test_generate_entry_ubuntu_series_numbering() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let repo_dir = temp_dir.path();
|
||||
setup_repo(repo_dir);
|
||||
let changelog_path = repo_dir.join("debian/changelog");
|
||||
std::fs::create_dir_all(repo_dir.join("debian")).unwrap();
|
||||
std::fs::write(
|
||||
&changelog_path,
|
||||
"mypackage (1.0-1) unstable; urgency=medium\n\n * Initial release\n\n -- Maintainer <m@e.com> Wed, 01 Jan 2020 00:00:00 +0000\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let entry = generate_entry(
|
||||
"debian/changelog",
|
||||
Some(repo_dir),
|
||||
None,
|
||||
Some("noble"),
|
||||
EntryKind::Normal,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(entry.new_version, "1.0-1ubuntu1");
|
||||
assert_eq!(entry.series, "noble");
|
||||
|
||||
// Re-bumping the now-Ubuntu changelog increments the counter
|
||||
let entry = generate_entry(
|
||||
"debian/changelog",
|
||||
Some(repo_dir),
|
||||
None,
|
||||
Some("noble"),
|
||||
EntryKind::Normal,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(entry.new_version, "1.0-1ubuntu2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_maintainer_info() {
|
||||
let _identity = IDENTITY_LOCK.blocking_lock();
|
||||
// Test with env vars
|
||||
unsafe {
|
||||
std::env::set_var("DEBFULLNAME", "Env Name");
|
||||
|
||||
@@ -712,6 +712,30 @@ pub async fn get_debian_series_number(series: &str) -> Result<Option<String>, Bo
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// The release number of a distribution series, paired with the dist it
|
||||
/// belongs to: the version column of the series data, stripped to its
|
||||
/// leading token ("12" for Debian bookworm, "26.04" out of Ubuntu
|
||||
/// resolute's "26.04 LTS"). `None` when the series carries no version at
|
||||
/// all (Debian's rolling sid/experimental have an empty column;
|
||||
/// pseudo-versions like "unstable" pass through, callers validate per
|
||||
/// vendor). Errors when no known distribution carries the series.
|
||||
pub async fn get_series_release_number(
|
||||
series: &str,
|
||||
) -> Result<Option<(String, String)>, Box<dyn Error>> {
|
||||
let dist = get_dist_from_series(series).await?;
|
||||
for info in get_ordered_series(&dist).await? {
|
||||
if info.series == series {
|
||||
let number = info
|
||||
.version
|
||||
.as_deref()
|
||||
.and_then(|version| version.split_whitespace().next())
|
||||
.map(str::to_string);
|
||||
return Ok(number.map(|number| (dist, number)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -1033,6 +1057,25 @@ mod tests {
|
||||
assert!(unknown_number.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_series_release_number() {
|
||||
let (dist, bookworm) = get_series_release_number("bookworm")
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(dist, "debian");
|
||||
assert_eq!(bookworm, "12");
|
||||
|
||||
// Ubuntu LTS rows carry a " LTS" decoration: only the leading
|
||||
// YY.MM token is the release number
|
||||
let (dist, noble) = get_series_release_number("noble").await.unwrap().unwrap();
|
||||
assert_eq!(dist, "ubuntu");
|
||||
assert_eq!(noble, "24.04");
|
||||
|
||||
// No known dist carries the series
|
||||
assert!(get_series_release_number("not-a-series").await.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_keyring_urls_sid() {
|
||||
// Test that 'sid' returns keyrings from the 3 latest released versions
|
||||
|
||||
+27
-6
@@ -7,8 +7,6 @@ use pkh::context::ContextConfig;
|
||||
|
||||
extern crate flate2;
|
||||
|
||||
use pkh::changelog::generate_entry;
|
||||
|
||||
use indicatif_log_bridge::LogWrapper;
|
||||
use log::{error, info};
|
||||
|
||||
@@ -178,8 +176,12 @@ fn main() {
|
||||
Command::new("chlog")
|
||||
.about("Auto-generate changelog entry, editing it, committing it afterwards")
|
||||
.arg(arg!(-s --series <series> "Target distribution series").required(false))
|
||||
.arg(arg!(--backport "This changelog is for a backport entry").required(false))
|
||||
.arg(arg!(-v --version <version> "Target version").required(false)),
|
||||
.arg(arg!(--backport "Number the entry as a backport of the target series (Debian: 1.0-1 becomes 1.0-1~bpo12+1; Ubuntu: 3.1-1ubuntu2 becomes 3.1-1ubuntu2~24.04.1)").required(false)
|
||||
.conflicts_with_all(["nmu", "rebuild"]))
|
||||
.arg(arg!(--nmu "Number the entry as a non-maintainer upload (1.0-1 becomes 1.0-1.1, native 1.0 becomes 1.0+nmu1)").required(false)
|
||||
.conflicts_with("rebuild"))
|
||||
.arg(arg!(--rebuild "Number the entry as a no-change rebuild (1.0-1 becomes 1.0-1build1)").required(false))
|
||||
.arg(arg!(-v --version <version> "Target version (overrides the --backport/--nmu/--rebuild numbering)").required(false)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("build")
|
||||
@@ -389,6 +391,23 @@ fn main() {
|
||||
let cwd = current_dir_or_exit();
|
||||
let version = sub_matches.get_one::<String>("version").map(|s| s.as_str());
|
||||
let cli_series = sub_matches.get_one::<String>("series").map(|s| s.as_str());
|
||||
let kind = if sub_matches
|
||||
.get_one::<bool>("backport")
|
||||
.copied()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
pkh::changelog::EntryKind::Backport
|
||||
} else if sub_matches.get_one::<bool>("nmu").copied().unwrap_or(false) {
|
||||
pkh::changelog::EntryKind::Nmu
|
||||
} else if sub_matches
|
||||
.get_one::<bool>("rebuild")
|
||||
.copied()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
pkh::changelog::EntryKind::Rebuild
|
||||
} else {
|
||||
pkh::changelog::EntryKind::Normal
|
||||
};
|
||||
|
||||
// Determine target series: CLI flag > interactive selector > current changelog series
|
||||
let target_series = if let Some(s) = cli_series {
|
||||
@@ -418,12 +437,13 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let entry = match generate_entry(
|
||||
let entry = match rt.block_on(pkh::changelog::generate_entry(
|
||||
"debian/changelog",
|
||||
Some(&cwd),
|
||||
version,
|
||||
target_series.as_deref(),
|
||||
) {
|
||||
kind,
|
||||
)) {
|
||||
Ok(entry) => entry,
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
@@ -434,6 +454,7 @@ fn main() {
|
||||
"Found package: {}, version: {}",
|
||||
entry.package, entry.previous_version
|
||||
);
|
||||
println!("New version: {}", entry.new_version);
|
||||
println!("Added new changelog entry to {}", entry.path.display());
|
||||
|
||||
let editor = match std::env::var("EDITOR") {
|
||||
|
||||
Reference in New Issue
Block a user