From 52c1bcca700ea202b5e2cf2aaf2ec6c105bae233 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 11 Jun 2025 09:29:36 +0200 Subject: [PATCH] only copy icons into repo/ if they changed This should make things more efficient and reduce the size of the diffs in the transparency log. Using shutil.copy2() preserves metadata. --- fdroidserver/common.py | 6 +++++- fdroidserver/update.py | 10 +++++----- tests/test_common.py | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index b131ce19..afdcc59e 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -55,6 +55,7 @@ environment variable to include. import copy import difflib from typing import List +import filecmp import git import glob import io @@ -812,7 +813,10 @@ def load_localized_config(name, repodir): icons_dir = os.path.join(repodir, 'icons') if not os.path.exists(icons_dir): os.makedirs(icons_dir, exist_ok=True) - shutil.copy(os.path.join("config", value), icons_dir) + src = os.path.join("config", value) + dest = os.path.join(icons_dir, os.path.basename(src)) + if not os.path.exists(dest) or not filecmp.cmp(src, dest): + shutil.copy2(src, dest) ret[afname][key][locale] = file_entry( os.path.join(icons_dir, value) ) diff --git a/fdroidserver/update.py b/fdroidserver/update.py index ba3b5506..95adfc66 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -20,6 +20,7 @@ # along with this program. If not, see . import argparse +import filecmp import sys import os import shutil @@ -1460,19 +1461,18 @@ def insert_localized_ios_app_metadata(apps_with_packages): fdroidserver.update.copy_ios_screenshots_to_repo(screenshots, package_name) # lookup icons, copy them and put them into app - icon_path = _get_ipa_icon(Path('build') / package_name) + icon_src = _get_ipa_icon(Path('build') / package_name) icon_dest = Path('repo') / package_name / 'icon.png' # for now just assume png - icon_stat = os.stat(icon_path) app['iconv2'] = { DEFAULT_LOCALE: { 'name': str(icon_dest).lstrip('repo'), 'sha256': common.sha256sum(icon_dest), - 'size': icon_stat.st_size, + 'size': os.path.getsize(icon_src), } } - if not icon_dest.exists(): + if not icon_dest.exists() or not filecmp.cmp(icon_src, icon_dest): icon_dest.parent.mkdir(parents=True, exist_ok=True) - shutil.copy(icon_path, icon_dest) + shutil.copy2(icon_src, icon_dest) def scan_repo_files(apkcache, repodir, knownapks, use_date_from_file=False): diff --git a/tests/test_common.py b/tests/test_common.py index 1f6ff947..57cbb416 100755 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -2815,6 +2815,46 @@ class CommonTest(SetUpTearDownMixin, unittest.TestCase): ) self.assertEqual(['en-US'], list(categories['GuardianProject']['name'].keys())) + def test_load_localized_config_copy_icon(self): + os.chdir(self.testdir) + os.mkdir('config') + Path('config/categories.yml').write_text('System:\n icon: system.png') + source_file = 'config/system.png' + Path(source_file).write_text('placeholder') + time.sleep(0.01) # ensure reliable failure if mtime isn't preserved + fdroidserver.common.load_localized_config(CATEGORIES_CONFIG_NAME, 'repo') + dest_file = f'repo/icons/{os.path.basename(source_file)}' + self.assertEqual(os.path.getsize(source_file), os.path.getsize(dest_file)) + self.assertEqual(os.path.getmtime(source_file), os.path.getmtime(dest_file)) + + def test_load_localized_config_copy_unchanged(self): + """The destination file should only change if the source file did.""" + os.chdir(self.testdir) + os.mkdir('config') + Path('config/categories.yml').write_text('System:\n icon: system.png') + source_file = 'config/system.png' + Path(source_file).write_text('placeholder') + fdroidserver.common.load_localized_config(CATEGORIES_CONFIG_NAME, 'repo') + delta = 0.01 + time.sleep(delta) # ensure reliable failure if file isn't preserved + fdroidserver.common.load_localized_config(CATEGORIES_CONFIG_NAME, 'repo') + dest_file = f'repo/icons/{os.path.basename(source_file)}' + self.assertAlmostEqual( + os.path.getctime(source_file), os.path.getctime(dest_file), delta=delta + ) + + def test_load_localized_config_copy_over_dest(self): + os.chdir(self.testdir) + os.mkdir('config') + Path('config/categories.yml').write_text('System:\n icon: system.png') + source_file = Path('config/system.png') + dest_file = Path(f'repo/icons/{os.path.basename(source_file)}') + source_file.write_text('placeholder') + dest_file.parent.mkdir(parents=True) + dest_file.write_text('different contents') + fdroidserver.common.load_localized_config(CATEGORIES_CONFIG_NAME, 'repo') + self.assertEqual(os.path.getsize(source_file), os.path.getsize(dest_file)) + def test_load_localized_config_0_file(self): os.chdir(self.testdir) os.mkdir('config')