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()