From 866daf27effab4ce13a1a9ff355c4f235ad39c45 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 7 Aug 2025 12:32:14 -0700 Subject: [PATCH] package: Refresh apt cache if sources list is newer - When backports repository or unstable repository freshly added by the updates app. We will like apps to become available due to newly available Debian packages. For this to happen 'apt update' must be called before checking if an app is available. Tests: - Freshly apply the patches for upgrades app. Setup is re-run and unstable sources file is introduced. Immediately visit the Matrix app and notice that is shown as available and can be installed immediately. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/package.py | 20 +++++++++++++++---- plinth/tests/test_package.py | 37 ++++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/plinth/package.py b/plinth/package.py index e189e84a1..ca7c56407 100644 --- a/plinth/package.py +++ b/plinth/package.py @@ -268,11 +268,23 @@ class Packages(app_module.FollowerComponent): except MissingPackageError: pass # We will retry after refreshing package list - # If the package cache is new, then package is really not available. package_cache = pathlib.Path('/var/cache/apt/pkgcache.bin') - if (package_cache.exists() - and time.time() - package_cache.stat().st_mtime < 3600): - return False + if package_cache.exists(): + package_cache_mtime = package_cache.stat().st_mtime + + sources_dir = pathlib.Path('/etc/apt/sources.list.d') + source_files = ([pathlib.Path('/etc/apt/sources.list')] + + list(sources_dir.glob('*.list')) + + list(sources_dir.glob('*.sources'))) + are_sources_newer = any( + source_file.stat().st_mtime > package_cache_mtime + for source_file in source_files) + + # If the package cache is new, then package is really not + # available. + if (time.time() - package_cache.stat().st_mtime < 3600 + and not are_sources_newer): + return False # Perform 'apt-get update' refresh_package_lists() diff --git a/plinth/tests/test_package.py b/plinth/tests/test_package.py index 137418a97..b7aea85c3 100644 --- a/plinth/tests/test_package.py +++ b/plinth/tests/test_package.py @@ -344,8 +344,19 @@ def test_packages_find_conflicts(packages_installed_): @patch('pathlib.Path') def test_packages_is_available(path_class, cache, refresh_package_lists): """Test checking for available packages.""" - path = Mock() - path_class.return_value = path + package_cache = Mock() + sources_dir = Mock() + sources_dir.glob.return_value = [] + sources_list = Mock() + + def path_side_effect(path): + return { + '/var/cache/apt/pkgcache.bin': package_cache, + '/etc/apt/sources.list.d': sources_dir, + '/etc/apt/sources.list': sources_list + }[path] + + path_class.side_effect = path_side_effect # Packages found in cache component = Packages('test-component', ['package1', 'package2']) @@ -356,22 +367,32 @@ def test_packages_is_available(path_class, cache, refresh_package_lists): # Packages not found, cache exists and is fresh cache.return_value = ['package1'] - path.exists.return_value = True - path.stat.return_value.st_mtime = time.time() + package_cache.exists.return_value = True + package_cache.stat.return_value.st_mtime = time.time() + sources_list.stat.return_value.st_mtime = time.time() - 100 assert not component.is_available() refresh_package_lists.assert_not_called() # Packages not found, cache does not exist cache.return_value = ['package1'] - path.exists.return_value = False + package_cache.exists.return_value = False assert not component.is_available() refresh_package_lists.assert_called_once() - # Packages not found, cache is stale + # Packages not found, cache is stale because it older than 1 hour cache.return_value = ['package1'] refresh_package_lists.reset_mock() - path.exists.return_value = True - path.stat.return_value.st_mtime = time.time() - 7200 + package_cache.exists.return_value = True + package_cache.stat.return_value.st_mtime = time.time() - 7200 + assert not component.is_available() + refresh_package_lists.assert_called_once() + + # Packages not found, cache is stale because it older than sources.list + cache.return_value = ['package1'] + refresh_package_lists.reset_mock() + package_cache.exists.return_value = True + package_cache.stat.return_value.st_mtime = time.time() - 100 + sources_list.stat.return_value.st_mtime = time.time() assert not component.is_available() refresh_package_lists.assert_called_once()