From 2c566cf68f40e91ee62ec20d72d5a9777a79d8ec Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 7 Jun 2023 13:36:02 +0200 Subject: [PATCH] update: add all categories in metadata files to repo definitions !1366 makes it so categories are now defined by the repo. Categories can be defined in the config so that lint has a list of categories to enforce. This also provides a place for localization and icons for the categories. The old way of defining categories was just listing them in app metadata files. This restores that way of functioning when using index-v2. closes #1137 --- fdroidserver/index.py | 11 ++++- tests/repo/entry.json | 4 +- tests/repo/index-v2.json | 7 ++- tests/update.TestCase | 94 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 5 deletions(-) diff --git a/fdroidserver/index.py b/fdroidserver/index.py index ddb51546..d05d159e 100644 --- a/fdroidserver/index.py +++ b/fdroidserver/index.py @@ -711,6 +711,7 @@ def make_v2(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_ output_packages = collections.OrderedDict() output['packages'] = output_packages + categories_used_by_apps = set() for package in packages: packageName = package['packageName'] if packageName not in apps: @@ -730,7 +731,9 @@ def make_v2(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_ else: packagelist = {} output_packages[packageName] = packagelist - packagelist["metadata"] = package_metadata(apps[packageName], repodir) + app = apps[packageName] + categories_used_by_apps.update(app.get('Categories', [])) + packagelist["metadata"] = package_metadata(app, repodir) if "signer" in package: packagelist["metadata"]["preferredSigner"] = package["signer"] @@ -738,6 +741,12 @@ def make_v2(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_ packagelist["versions"][package["hash"]] = convert_version(package, apps[packageName], repodir) + if categories_used_by_apps and not output['repo'].get(CATEGORIES_CONFIG_NAME): + output['repo'][CATEGORIES_CONFIG_NAME] = dict() + for category in sorted(categories_used_by_apps): + if category not in output['repo'][CATEGORIES_CONFIG_NAME]: + output['repo'][CATEGORIES_CONFIG_NAME][category] = dict() + entry = {} entry["timestamp"] = repodict["timestamp"] diff --git a/tests/repo/entry.json b/tests/repo/entry.json index 5c0ca528..0e8828e0 100644 --- a/tests/repo/entry.json +++ b/tests/repo/entry.json @@ -3,8 +3,8 @@ "version": 20002, "index": { "name": "/index-v2.json", - "sha256": "7117ee6ff4ff2dd71ec3f3d3ad2ef7e9fd4afead9b1f2d39d0b224a1812e78b5", - "size": 53233, + "sha256": "b613858aa7a2ec476fcef5c841a5b8ff4b3b0f67f07678da981e2843f49c71ba", + "size": 53283, "numPackages": 10 }, "diffs": {} diff --git a/tests/repo/index-v2.json b/tests/repo/index-v2.json index 6ea92407..f45c1514 100644 --- a/tests/repo/index-v2.json +++ b/tests/repo/index-v2.json @@ -533,7 +533,10 @@ "name": { "en-US": "System" } - } + }, + "1": {}, + "2.0": {}, + "tests": {} }, "requests": { "install": [ @@ -1443,4 +1446,4 @@ } } } -} +} \ No newline at end of file diff --git a/tests/update.TestCase b/tests/update.TestCase index 391dec93..e859e04f 100755 --- a/tests/update.TestCase +++ b/tests/update.TestCase @@ -53,6 +53,7 @@ import fdroidserver.common import fdroidserver.exception import fdroidserver.metadata import fdroidserver.update +from fdroidserver.common import CATEGORIES_CONFIG_NAME DONATION_FIELDS = ('Donate', 'Liberapay', 'OpenCollective') @@ -1804,6 +1805,99 @@ class UpdateTest(unittest.TestCase): fdroidserver.update.main() self.assertFalse(categories_txt.exists()) + def test_no_blank_auto_defined_categories(self): + """When no app has Categories, there should be no definitions in the repo.""" + os.chdir(self.testdir) + os.mkdir('metadata') + os.mkdir('repo') + Path('config.yml').write_text( + 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff' + ) + + testapk = os.path.join('repo', 'com.politedroid_6.apk') + shutil.copy(os.path.join(self.basedir, testapk), testapk) + Path('metadata/com.politedroid.yml').write_text('Name: Polite') + + with mock.patch('sys.argv', ['fdroid update', '--delete-unknown', '--nosign']): + fdroidserver.update.main() + with open('repo/index-v2.json') as fp: + index = json.load(fp) + self.assertFalse(CATEGORIES_CONFIG_NAME in index['repo']) + + def test_auto_defined_categories(self): + """Repos that don't define categories in config/ should use auto-generated.""" + os.chdir(self.testdir) + os.mkdir('metadata') + os.mkdir('repo') + Path('config.yml').write_text( + 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff' + ) + + testapk = os.path.join('repo', 'com.politedroid_6.apk') + shutil.copy(os.path.join(self.basedir, testapk), testapk) + Path('metadata/com.politedroid.yml').write_text('Categories: [Time]') + + with mock.patch('sys.argv', ['fdroid update', '--delete-unknown', '--nosign']): + fdroidserver.update.main() + with open('repo/index-v2.json') as fp: + index = json.load(fp) + self.assertEqual( + {'Time': dict()}, + index['repo'][CATEGORIES_CONFIG_NAME], + ) + + def test_auto_defined_categories_two_apps(self): + """Repos that don't define categories in config/ should use auto-generated.""" + os.chdir(self.testdir) + os.mkdir('metadata') + os.mkdir('repo') + Path('config.yml').write_text( + 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff' + ) + + testapk = os.path.join('repo', 'com.politedroid_6.apk') + shutil.copy(os.path.join(self.basedir, testapk), testapk) + Path('metadata/com.politedroid.yml').write_text('Categories: [bar]') + testapk = os.path.join('repo', 'souch.smsbypass_9.apk') + shutil.copy(os.path.join(self.basedir, testapk), testapk) + Path('metadata/souch.smsbypass.yml').write_text('Categories: [foo, bar]') + + with mock.patch('sys.argv', ['fdroid update', '--delete-unknown', '--nosign']): + fdroidserver.update.main() + with open('repo/index-v2.json') as fp: + index = json.load(fp) + self.assertEqual( + {'bar': dict(), 'foo': dict()}, + index['repo'][CATEGORIES_CONFIG_NAME], + ) + + def test_auto_defined_categories_mix_into_config_categories(self): + """Repos that don't define all categories in config/ also use auto-generated.""" + os.chdir(self.testdir) + os.mkdir('config') + Path('config/categories.yml').write_text('System: {name: System Apps}') + os.mkdir('metadata') + os.mkdir('repo') + Path('config.yml').write_text( + 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff' + ) + + testapk = os.path.join('repo', 'com.politedroid_6.apk') + shutil.copy(os.path.join(self.basedir, testapk), testapk) + Path('metadata/com.politedroid.yml').write_text('Categories: [Time]') + testapk = os.path.join('repo', 'souch.smsbypass_9.apk') + shutil.copy(os.path.join(self.basedir, testapk), testapk) + Path('metadata/souch.smsbypass.yml').write_text('Categories: [System, Time]') + + with mock.patch('sys.argv', ['fdroid update', '--delete-unknown', '--nosign']): + fdroidserver.update.main() + with open('repo/index-v2.json') as fp: + index = json.load(fp) + self.assertEqual( + {'System': {'name': {'en-US': 'System Apps'}}, 'Time': dict()}, + index['repo'][CATEGORIES_CONFIG_NAME], + ) + if __name__ == "__main__": os.chdir(os.path.dirname(__file__))