Sunil Mohan Adapa fecd6a3577
upgrades: Overhaul detection of distribution
- Move some utilities to utils.py from distupgrade.py and __init__.py.

- This fixes issues with apt preferences being set on unstable
distribution (despite code that tries to prevent it).

- There is no way to distinguish between 'testing' and 'unstable' distributions
in Debian using commands like lsb_release (powered by /etc/os-release). See:
https://lwn.net/Articles/984635/ . So, use the value set in
/etc/apt/sources.list.

Tests: (tested entire patchset)

- Deluge can be installed in trixie.

- Auto-distribution upgrade button is checked during setup on stable and
oldstable but not on testing and unstable.

- Auto-distribution upgrade button is enabled in the form on stable and
oldstable but not on testing and unstable.

- Backports wizard step is skipped on unstable (non-develop mode), but not on
oldstable, stable, testing, and unstable (develop mode).

- If backports are not activated during first wizard, then backports can be
activated on upgrades app page if distribution is oldstable, stable, testing, or
unstable (non-develop mode) but not unstable (develop mode).

- During re-run of setup, setting up backport sources is skipped if already
setup.

- Backports sources files are not added in testing (non-develop) and
unstable (non-develop) distributions. Backports sources are added to oldstable,
stable, testing (develop) and unstable (develop). Unstable sources sources are
not added to unstable but added to oldstable, stable, and testing.

- Backports sources file is added with correct code name bookworm/trixie for
oldstable, stable, and testing distributions.

- When backports sources is set to 'bookworm-backports' on Trixie distribution,
re-running setup updates them to 'trixie-backports'.

- Preferences files are added in oldstable, stable, and testing distributions
but not unstable.

- If unstable and another distro is present in apt sources, then it is treated
as unstable as shown in the distribution upgrade page.

- Current codename is shown properly from sources.list in oldstable, stable,
testing, and unstable in distribution upgrade page.

- NOT TESTED: If distribution upgrade is interrupted, then continue page is
shown.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2025-09-08 19:33:33 -04:00

127 lines
3.6 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""Utilities for regular updates and dist-upgrades."""
import pathlib
import re
import subprocess
import augeas
from plinth.modules.apache.components import check_url
RELEASE_FILE_URL = \
'https://deb.debian.org/debian/dists/{}/Release'
DIST_UPGRADE_REQUIRED_FREE_SPACE = 5000000
sources_list = pathlib.Path('/etc/apt/sources.list')
def check_auto() -> bool:
"""Return whether automatic updates are enabled."""
arguments = [
'apt-config', 'shell', 'UpdateInterval',
'APT::Periodic::Update-Package-Lists'
]
output = subprocess.check_output(arguments).decode()
update_interval = 0
match = re.match(r"UpdateInterval='(.*)'", output)
if match:
update_interval = int(match.group(1))
return bool(update_interval)
def get_http_protocol() -> str:
"""Return the protocol to use for newly added repository sources."""
try:
from plinth.modules.torproxy import utils
if utils.is_apt_transport_tor_enabled():
return 'tor+http'
except Exception:
pass
return 'http'
def is_release_file_available(protocol: str, dist: str,
backports=False) -> bool:
"""Return whether the release for dist[-backports] is available."""
wrapper = None
if protocol == 'tor+http':
wrapper = 'torsocks'
if backports:
dist += '-backports'
try:
return check_url(RELEASE_FILE_URL.format(dist), wrapper=wrapper)
except FileNotFoundError:
return False
def is_sufficient_free_space() -> bool:
"""Return whether there is sufficient free space for dist upgrade."""
output = subprocess.check_output(['df', '--output=avail', '/'])
free_space = int(output.decode().split('\n')[1])
return free_space >= DIST_UPGRADE_REQUIRED_FREE_SPACE
def get_sources_list_codename() -> str | None:
"""Return the codename set in the /etc/apt/sources.list file."""
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
augeas.Augeas.NO_MODL_AUTOLOAD)
aug.transform('aptsources', str(sources_list))
aug.set('/augeas/context', '/files' + str(sources_list))
aug.load()
dists = set()
for match_ in aug.match('*'):
dist = aug.get(match_ + '/distribution')
if not dist:
continue
dist = dist.removesuffix('-updates')
dist = dist.removesuffix('-security')
dists.add(dist)
if 'unstable' in dists or 'sid' in dists:
return 'unstable'
if 'testing' in dists:
return 'testing'
# Multiple distributions are not understood.
if len(dists) != 1:
return None
return dists.pop()
def get_current_release():
"""Return current release and codename as a tuple."""
output = subprocess.check_output(
['lsb_release', '--release', '--codename',
'--short']).decode().strip()
lines = output.split('\n')
return lines[0], lines[1]
def is_distribution_unstable() -> bool:
"""Return whether the current distribution is unstable.
There is no way to distinguish between 'testing' and 'unstable'
distributions in Debian using commands like lsb_release (powered by
/etc/os-release). See: https://lwn.net/Articles/984635/ . So, use the value
set in /etc/apt/sources.list.
"""
codename = get_sources_list_codename()
return codename in ['unstable', 'sid']
def is_distribution_rolling() -> bool:
"""Return whether the current distribution is testing or unstable."""
# Release will be 'n/a' in latest unstable and testing distributions.
release, _ = get_current_release()
return release in ['unstable', 'testing', 'n/a']