diff --git a/fdroidserver/metadata.py b/fdroidserver/metadata.py index 4a818554..11549865 100644 --- a/fdroidserver/metadata.py +++ b/fdroidserver/metadata.py @@ -1033,6 +1033,53 @@ def post_parse_yaml_metadata(yamldata): } +def _format_stringmap(appid, field, stringmap, versionCode=None): + """Format TYPE_STRINGMAP taking into account localized files in the metadata dir. + + If there are any localized versions on the filesystem already, + then move them all there. Otherwise, keep them in the .yml file. + + The directory for the localized files that is named after the + field is all lower case, following the convention set by Fastlane + metadata, and used by fdroidserver. + + """ + app_dir = Path('metadata', appid) + try: + next(app_dir.glob('*/%s/*.txt' % field.lower())) + files = [] + for name, descdict in stringmap.items(): + for locale, desc in descdict.items(): + outdir = app_dir / locale / field.lower() + if versionCode: + filename = '%d_%s.txt' % (versionCode, name) + else: + filename = '%s.txt' % name + outfile = outdir / filename + files.append(str(outfile)) + with outfile.open('w') as fp: + fp.write(desc) + logging.warning( + _('Moving Anti-Features declarations to localized files:') + + '\n' + + '\n'.join(sorted(files)) + ) + return + except StopIteration: + pass + make_list = True + outlist = [] + for name in sorted(stringmap): + outlist.append(name) + descdict = stringmap.get(name) + if descdict and any(descdict.values()): + make_list = False + break + if make_list: + return outlist + return stringmap + + def _del_duplicated_NoSourceSince(app): # noqa: D403 NoSourceSince is the word. """NoSourceSince gets auto-added to AntiFeatures, but can also be manually added.""" @@ -1080,8 +1127,13 @@ def _builds_to_yaml(app): ] typ = flagtype(field) # don't check value == True for TYPE_INT as it could be 0 - if value is not None and (typ == TYPE_INT or value): + if value and typ == TYPE_STRINGMAP: + v = _format_stringmap(app['id'], field, value, build['versionCode']) + if v: + b[field] = v + elif value is not None and (typ == TYPE_INT or value): b.update({field: _field_to_yaml(typ, value)}) + builds.append(b) # insert extra empty lines between build entries @@ -1107,6 +1159,10 @@ def _app_to_yaml(app): cm.update({field: _builds_to_yaml(app)}) elif field == 'CurrentVersionCode': cm[field] = _field_to_yaml(TYPE_INT, value) + elif field == 'AntiFeatures': + v = _format_stringmap(app['id'], field, value) + if v: + cm[field] = v elif field == 'AllowedAPKSigningKeys': value = [str(i).lower() for i in value] if len(value) == 1: diff --git a/fdroidserver/rewritemeta.py b/fdroidserver/rewritemeta.py index b1932502..5a1a6ea0 100644 --- a/fdroidserver/rewritemeta.py +++ b/fdroidserver/rewritemeta.py @@ -99,6 +99,7 @@ def main(): print(path) continue + # TODO these should be moved to metadata.write_yaml() builds = remove_blank_flags_from_builds(app.get('Builds')) if builds: app['Builds'] = builds diff --git a/tests/metadata-rewrite-yml/app.with.special.build.params.yml b/tests/metadata-rewrite-yml/app.with.special.build.params.yml index a31604ae..eeb174b7 100644 --- a/tests/metadata-rewrite-yml/app.with.special.build.params.yml +++ b/tests/metadata-rewrite-yml/app.with.special.build.params.yml @@ -1,5 +1,5 @@ AntiFeatures: - UpstreamNonFree: {} + - UpstreamNonFree Categories: - System License: GPL-3.0-only diff --git a/tests/metadata.TestCase b/tests/metadata.TestCase index bfad7d28..fb17c6df 100755 --- a/tests/metadata.TestCase +++ b/tests/metadata.TestCase @@ -1296,6 +1296,87 @@ class MetadataTest(unittest.TestCase): ), ) + def test_write_yaml_build_antifeatures(self): + mf = io.StringIO() + app = metadata.App( + { + 'License': 'Apache-2.0', + 'Builds': [ + metadata.Build( + { + 'versionCode': 102030, + 'versionName': 'v1.2.3', + 'gradle': ['yes'], + 'antifeatures': { + 'a': {}, + 'b': {'de': 'Probe', 'en-US': 'test'}, + }, + } + ), + ], + 'id': 'placeholder', + } + ) + metadata.write_yaml(mf, app) + mf.seek(0) + self.assertEqual( + mf.read(), + textwrap.dedent( + """\ + License: Apache-2.0 + + Builds: + - versionName: v1.2.3 + versionCode: 102030 + gradle: + - yes + antifeatures: + a: {} + b: + de: Probe + en-US: test + """ + ), + ) + + def test_write_yaml_build_antifeatures_old_style(self): + mf = io.StringIO() + app = metadata.App( + { + 'License': 'Apache-2.0', + 'Builds': [ + metadata.Build( + { + 'versionCode': 102030, + 'versionName': 'v1.2.3', + 'gradle': ['yes'], + 'antifeatures': {'b': {}, 'a': {}}, + } + ), + ], + 'id': 'placeholder', + } + ) + metadata.write_yaml(mf, app) + mf.seek(0) + self.assertEqual( + mf.read(), + textwrap.dedent( + """\ + License: Apache-2.0 + + Builds: + - versionName: v1.2.3 + versionCode: 102030 + gradle: + - yes + antifeatures: + - a + - b + """ + ), + ) + def test_write_yaml_make_sure_provides_does_not_get_written(self): mf = io.StringIO() app = fdroidserver.metadata.App() @@ -1649,7 +1730,7 @@ class MetadataTest(unittest.TestCase): textwrap.dedent( """\ AntiFeatures: - NonFreeNet: {} + - NonFreeNet Categories: - Time License: GPL-3.0-only @@ -1669,9 +1750,9 @@ class MetadataTest(unittest.TestCase): commit: 6a548e4b19 target: android-10 antifeatures: - KnownVuln: {} - UpstreamNonFree: {} - NonFreeAssets: {} + - KnownVuln + - NonFreeAssets + - UpstreamNonFree ArchivePolicy: 4 versions AutoUpdateMode: Version v%v @@ -1727,6 +1808,56 @@ class MetadataTest(unittest.TestCase): cm = metadata._app_to_yaml(metadata.App({'CurrentVersionCode': 0})) self.assertFalse('CurrentVersionCode' in cm) + def test_format_stringmap_empty(self): + self.assertEqual( + metadata._format_stringmap('πŸ”₯', 'test', dict()), + list(), + ) + + def test_format_stringmap_one_list(self): + self.assertEqual( + metadata._format_stringmap('πŸ”₯', 'test', {'Tracking': {}, 'Ads': {}}), + ['Ads', 'Tracking'], + ) + + def test_format_stringmap_one_list_empty_desc(self): + self.assertEqual( + metadata._format_stringmap('πŸ”₯', 'test', {'NonFree': {}, 'Ads': {'en': ''}}), + ['Ads', 'NonFree'], + ) + + def test_format_stringmap_three_list(self): + self.assertEqual( + metadata._format_stringmap('πŸ”₯', 'test', {'B': {}, 'A': {}, 'C': {}}), + ['A', 'B', 'C'], + ) + + def test_format_stringmap_two_dict(self): + self.assertEqual( + metadata._format_stringmap('πŸ”₯', 'test', {'1': {'uz': 'a'}, '2': {}}), + {'1': {'uz': 'a'}, '2': {}}, + ) + + def test_format_stringmap_three_locales(self): + self.assertEqual( + metadata._format_stringmap( + 'πŸ”₯', 'test', {'AF': {'uz': 'a', 'ko': 'b', 'zh': 'c'}} + ), + {'AF': {'ko': 'b', 'uz': 'a', 'zh': 'c'}}, + ) + + def test_format_stringmap_move_build_antifeatures_to_filesystem(self): + os.chdir(self.testdir) + appid = 'a' + yml = Path('metadata/a.yml') + yml.parent.mkdir() + self.assertEqual( + metadata._format_stringmap( + appid, 'antifeatures', {'AF': {'uz': 'a', 'ko': 'b', 'zh': 'c'}} + ), + {'AF': {'ko': 'b', 'uz': 'a', 'zh': 'c'}}, + ) + class PostMetadataParseTest(unittest.TestCase): """Test the functions that post process the YAML input. diff --git a/tests/rewritemeta.TestCase b/tests/rewritemeta.TestCase index 5e6cda33..244ad9a0 100755 --- a/tests/rewritemeta.TestCase +++ b/tests/rewritemeta.TestCase @@ -180,6 +180,30 @@ class RewriteMetaTest(unittest.TestCase): }, ) + def test_remove_blank_flags_from_builds_app_with_special_build_params_af(self): + """Unset fields in Builds: entries should be removed.""" + appid = 'app.with.special.build.params' + app = metadata.read_metadata({appid: -1})[appid] + builds = rewritemeta.remove_blank_flags_from_builds(app.get('Builds')) + self.assertEqual( + builds[-2], + { + 'antifeatures': { + 'Ads': {'en-US': 'includes ad lib\n', 'zh-CN': 'εŒ…ζ‹¬εΉΏε‘Šε›ΎδΉ¦ι¦†\n'}, + 'Tracking': {'en-US': 'standard suspects\n'}, + }, + 'commit': '2.1.1', + 'maven': '2', + 'patch': [ + 'manifest-ads.patch', + 'mobilecore.patch', + ], + 'srclibs': ['FacebookSDK@sdk-version-3.0.2'], + 'versionCode': 50, + 'versionName': '2.1.1-c', + }, + ) + def test_rewrite_scenario_trivial(self): sys.argv = ['rewritemeta', 'a', 'b']