#1147514 python3-apt: change_server() rewrites Debian's separate security archive onto the main mirror

Package:
python3-apt
Source:
python3-apt
Description:
Python 3 interface to libapt-pkg
Submitter:
Emfox Zhou
Date:
2026-09-12 15:09:03 UTC
Severity:
normal
Tags:
#1147514#5
Date:
2026-09-12 15:07:55 UTC
From:
To:
Dear Maintainer,

This is the underlying fix for Debian bug #1038747[1]. That bug is currently
filed against software-properties, but the actual defect lives in python3-apt:
aptsources.distro.Distribution.change_server() rewrites *every* child source's
URI onto the newly selected main mirror. On Debian that is wrong for the
security archive, which is a SEPARATE archive (deb.debian.org/debian-security)
rather than a pocket of the main archive (as it is on Ubuntu).

Root cause
----------
In aptsources/distro.py, change_server() does (for child sources):

    if (source.template.base_uri is None
            or source.template.base_uri != source.uri):
        change_server_of_source(source, uri, seen_binary)

This replaces the whole child URI with the main mirror URI. For a Debian
security source the template's base_uri is security.debian.org while the
source URI is deb.debian.org/debian-security; the old guard only skips the
source when base_uri already equals the current URI, so the security source
is moved to <mirror>/debian/<suite>-security, which does not exist.

Result: after changing the download mirror in the "Software & Updates" GUI,
`apt update` fails with

    E: The repository 'https://deb.debian.org/debian bookworm-security
       Release' does not have a Release file.

and the entire security-update channel is disabled. The deb-src counterpart
is affected the same way via the source_code_sources loop.

Fix
---
Introduce an `_archive_root()` helper (host + first path segment) and a
`separate_archive()` predicate that compares the child source's template
base_uri against the main archive's base_uri. Child sources (and their
deb-src siblings) that live on a separate archive are left untouched when the
main download server is changed, while same-archive children such as
*-updates keep following the mirror. Ubuntu behaviour is unchanged because
its -security shares the main archive.

Verification
------------
I reproduced the bug and confirmed the fix on a current Debian sid container
(python3-apt 3.1.0). The attached hermetic test(AI generated) drives
change_server()
directly with an in-memory Debian sid source set (no /etc/apt access, no
network):

  BEFORE (unpatched 3.1.0):
    main         -> http://ftp.hu.debian.org/debian/        (follows mirror, ok)
    security     -> http://ftp.hu.debian.org/debian/        (WRONG:
moved off debian-security)
    security-src -> http://ftp.hu.debian.org/debian/        (WRONG)
    RESULT: FAIL

  AFTER (patched):
    main         -> http://ftp.hu.debian.org/debian/        (follows mirror, ok)
    security     -> http://deb.debian.org/debian-security   (kept)
    security-src -> http://deb.debian.org/debian-security   (kept)
    RESULT: PASS

The patch below applies cleanly to sid's 3.1.0.

Kind regards,
Emfox Zhou

[1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1038747