os-apply: tell an UPDATED conffile from a KEPT one (dpkg's two shapes, measured live on demo-hp)
gates / gates (push) Successful in 19s
gates / gates (push) Successful in 19s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
+13
-2
@@ -389,10 +389,21 @@ class Apply:
|
||||
t0 = time.time()
|
||||
rc, out, err = self.g(APT_ENV + ["apt-get", "-y", "-q"] + DPKG_OPTS + args)
|
||||
secs = time.time() - t0
|
||||
# dpkg says "Installing new version of config file X" when X was NOT changed locally (the package's new
|
||||
# version is taken), and "Configuration file 'X'" + "Keeping old config file" when it was (--force-confold
|
||||
# keeps the local one; the package's version lands as X.dpkg-dist). Measured live 2026-10-04 (debian_version).
|
||||
conflict = None
|
||||
for l in (out + err).splitlines():
|
||||
m = re.search(r"Installing new version of config file (\S+)|Configuration file '([^']+)'", l)
|
||||
m = re.search(r"Installing new version of config file (\S+?)\s*\.\.\.", l)
|
||||
if m:
|
||||
log(f"os-apply: CONFFILE kept {m.group(1) or m.group(2)}")
|
||||
log(f"os-apply: CONFFILE updated {m.group(1)} (it was not changed locally)")
|
||||
m = re.search(r"Configuration file '([^']+)'", l)
|
||||
if m:
|
||||
conflict = m.group(1)
|
||||
if conflict and "Keeping old config file" in l:
|
||||
log(f"os-apply: CONFFILE kept {conflict} (changed locally; the package's version is {conflict}.dpkg-dist)")
|
||||
self.report.setdefault("conffiles_kept", []).append(conflict)
|
||||
conflict = None
|
||||
self.g(["apt-get", "clean"])
|
||||
if rc != 0:
|
||||
_, aud, _ = self.g(["dpkg", "--audit"])
|
||||
|
||||
@@ -136,7 +136,7 @@ class Fake:
|
||||
if "=" in x and not x.startswith("-") and "::" not in x:
|
||||
n, v = x.split("=", 1)
|
||||
self.installed[n] = v
|
||||
return 0, "Setting up libc6 ...\n", ""
|
||||
return 0, getattr(self, "install_out", "Setting up libc6 ...\n"), ""
|
||||
if cmd == "df":
|
||||
return 0, f"Avail\n{self.free}\n", ""
|
||||
if cmd == "docker":
|
||||
@@ -438,6 +438,21 @@ class Refusals(unittest.TestCase):
|
||||
self.refused(f, "R13")
|
||||
|
||||
|
||||
class Conffiles(unittest.TestCase):
|
||||
# dpkg's two shapes, measured live 2026-10-04: an unchanged file is UPDATED; a locally changed one is KEPT.
|
||||
def test_updated_vs_kept(self):
|
||||
f = Fake()
|
||||
f.install_out = ("Installing new version of config file /etc/debian_version ...\n"
|
||||
"Configuration file '/etc/ssh/sshd_config'\n ==> Modified (by you or by a script) since installation.\n"
|
||||
" ==> Keeping old config file as default.\n")
|
||||
rc, rep = run(f)
|
||||
self.assertEqual(rc, 0, rep)
|
||||
self.assertIn("os-apply: CONFFILE updated /etc/debian_version (it was not changed locally)", f.logs)
|
||||
self.assertTrue(any(l.startswith("os-apply: CONFFILE kept /etc/ssh/sshd_config") for l in f.logs), f.logs)
|
||||
self.assertEqual(rep["conffiles_kept"], ["/etc/ssh/sshd_config"])
|
||||
self.assertFalse(any("kept /etc/debian_version" in l for l in f.logs), "an updated file must not be reported as kept")
|
||||
|
||||
|
||||
class Failure(unittest.TestCase):
|
||||
def test_install_failure_is_rc3_with_dpkg_state(self):
|
||||
f = Fake()
|
||||
|
||||
Reference in New Issue
Block a user