use crate::declare_detector;
use crate::diagnostic::{Action, Diagnostic, RunCommandAction};
use crate::{FixerError, FixerPreferences, LintianIssue, Visibility};
use debian_workspace::Workspace;
use std::path::PathBuf;
fn override_from(preferences: &FixerPreferences, name: &str) -> Option<String> {
preferences
.extra_env
.as_ref()
.and_then(|e| e.get(name).cloned())
}
pub fn detect(
ws: &dyn Workspace,
preferences: &FixerPreferences,
) -> Result<Vec<Diagnostic>, FixerError> {
let po_dir_rel = PathBuf::from("debian/po");
let entries = match ws.list_dir(&po_dir_rel)? {
Some(e) => e,
None => return Ok(Vec::new()),
};
let info = if entries.iter().any(|n| n == "templates.pot") {
vec!["[debian/po/templates.pot]".to_string()]
} else {
vec![]
};
let issue =
LintianIssue::source_with_info("newer-debconf-templates", Visibility::Warning, info);
let updatepo =
override_from(preferences, "DEBCONF_UPDATEPO").unwrap_or_else(|| "debconf-updatepo".into());
let argv = if let Some(timestamp_str) =
override_from(preferences, "DEBCONF_GETTEXTIZE_TIMESTAMP")
{
let timestamp: i64 = timestamp_str
.parse()
.map_err(|_| FixerError::Other("Invalid DEBCONF_GETTEXTIZE_TIMESTAMP".to_string()))?;
let dt = chrono::DateTime::from_timestamp(timestamp, 0)
.ok_or_else(|| FixerError::Other("Invalid timestamp".to_string()))?;
let formatted = dt.format("%Y-%m-%d %H:%M+0000").to_string();
let script = format!(
"set -e\n\
before=$(mktemp -d)\n\
cp -a debian/po/. \"$before\"/\n\
{}\n\
for f in debian/po/*; do\n\
[ -f \"$f\" ] || continue\n\
if ! cmp -s \"$f\" \"$before/$(basename \"$f\")\" 2>/dev/null; then\n\
sed -i \"s|^\\\"POT-Creation-Date: .*|\\\"POT-Creation-Date: {}\\\\\\\\n\\\"|\" \"$f\"\n\
fi\n\
done\n\
rm -rf \"$before\"\n",
updatepo, formatted
);
vec!["sh".into(), "-c".into(), script]
} else {
vec![updatepo]
};
Ok(vec![Diagnostic::with_actions(
issue,
"Debconf templates are newer than translations.".to_string(),
"Run debconf-updatepo after template changes.".to_string(),
vec![Action::RunCommand(RunCommandAction::Run {
argv,
scope: po_dir_rel,
env: Vec::new(),
})],
)])
}
declare_detector! {
name: "newer-debconf-templates",
tags: ["newer-debconf-templates"],
triggers: [
debian_workspace::Trigger::Glob("debian/po/*"),
],
detect: |ws, prefs| detect(ws, prefs),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::detector::Detector;
use crate::{FixerPreferences, Version};
use std::fs;
use std::path::Path;
use tempfile::TempDir;
fn run_apply_with(
base: &Path,
preferences: &FixerPreferences,
) -> Result<crate::FixerResult, FixerError> {
let v: Version = "1.0".parse().unwrap();
let adapter = DetectorImpl;
let ws = debian_workspace::fs_workspace::FsWorkspace::new(
base,
Some("test".into()),
Some(v.clone()),
);
adapter.apply(&ws, preferences)
}
fn run_apply(base: &Path) -> Result<crate::FixerResult, FixerError> {
run_apply_with(base, &FixerPreferences::default())
}
#[test]
fn test_no_po_dir() {
let tmp = TempDir::new().unwrap();
assert!(matches!(run_apply(tmp.path()), Err(FixerError::NoChanges)));
}
#[test]
fn test_missing_debconf_updatepo() {
let tmp = TempDir::new().unwrap();
let po_dir = tmp.path().join("debian/po");
fs::create_dir_all(&po_dir).unwrap();
fs::write(po_dir.join("POTFILES.in"), "").unwrap();
let missing = tmp.path().join("nonexistent-debconf-updatepo");
let mut extra_env = std::collections::HashMap::new();
extra_env.insert(
"DEBCONF_UPDATEPO".to_string(),
missing.to_str().unwrap().to_string(),
);
let preferences = FixerPreferences {
extra_env: Some(extra_env),
..Default::default()
};
match run_apply_with(tmp.path(), &preferences) {
Err(FixerError::MissingDependency(name)) => {
assert_eq!(name, missing.to_str().unwrap())
}
other => panic!("expected MissingDependency, got {:?}", other),
}
}
}