diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 21b97de1..d6bb170a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -61,6 +61,7 @@ metadata_v0: GIT_DEPTH: 1000 RELEASE_COMMIT_ID: d172d991803b27ad5213d11b5a737e5cb9e5fc3a # after switching to `git rev-parse` script: + - apt update -yyq && apt install -yyq yq - git fetch https://gitlab.com/fdroid/fdroidserver.git $RELEASE_COMMIT_ID - cd tests - export GITCOMMIT=$(git rev-parse HEAD) @@ -82,6 +83,10 @@ metadata_v0: metadata/dump_*/*.yaml # remove leading zeros (because we can't deal with that reliably) - 'sed -i ''s/: 0*\([1-9][0-9]*\)/: \1/g'' metadata/dump_*/*.yaml' + # if .Donate is an array set .Donate to the first element in the array otherwise leave it unchanged + # (running this in batches significantly speeds the gitlab.ci run) + # TODO: remove this when tests need to migrate + - find metadata/dump_*/ -name '*.yaml' -print0 | xargs -0 -P 4 -n 50 -- yq -y --in-place '.Donate |= if type == "array" then .[0] else . end' - diff -uw metadata/dump_* .apt-template: &apt-template diff --git a/fdroidserver/index.py b/fdroidserver/index.py index f66f9aea..7d156bac 100644 --- a/fdroidserver/index.py +++ b/fdroidserver/index.py @@ -607,7 +607,7 @@ def package_metadata(app, repodir): del meta["license"] if app["Donate"]: - meta["donate"] = [app["Donate"]] + meta["donate"] = app["Donate"] # TODO handle different resolutions if app.get("icon"): @@ -991,6 +991,9 @@ def make_v1(apps, packages, repodir, repodict, requestsdict, signer_fingerprints v = str(v) elif k == 'CurrentVersion': # TODO make SuggestedVersionName the canonical name k = 'suggestedVersionName' + elif k == "Donate": + k = "donate" + v = _first(v) else: k = k[:1].lower() + k[1:] d[k] = v @@ -1327,7 +1330,7 @@ def make_v0(apps, apks, repodir, repodict, requestsdict, signer_fingerprints): addElementNonEmpty('changelog', app.Changelog, doc, apel) addElementNonEmpty('author', app.AuthorName, doc, apel) addElementNonEmpty('email', app.AuthorEmail, doc, apel) - addElementNonEmpty('donate', app.Donate, doc, apel) + addElementNonEmpty('donate', _first(app.Donate), doc, apel) addElementNonEmpty('bitcoin', app.Bitcoin, doc, apel) addElementNonEmpty('litecoin', app.Litecoin, doc, apel) addElementNonEmpty('openCollective', app.OpenCollective, doc, apel) @@ -2116,3 +2119,10 @@ def make_altstore(apps, apks, config, repodir, pretty=False): with open(Path(repodir) / 'altstore-index.json', "w", encoding="utf-8") as f: json.dump(idx, f, indent=indent) + + +def _first(items, default_value=None): + try: + return items[0] + except (IndexError, TypeError): + return default_value diff --git a/fdroidserver/lint.py b/fdroidserver/lint.py index 414c6e2f..5f497395 100644 --- a/fdroidserver/lint.py +++ b/fdroidserver/lint.py @@ -1782,6 +1782,12 @@ def check_regexes(app): for line in v.splitlines(): if m.match(line): yield "%s at line '%s': %s" % (f, line, r) + elif t == metadata.TYPE_LIST: + for item in v or []: + if item is None: + continue + if m.match(item): + yield "%s '%s': %s" % (f, item, r) else: if v is None: continue diff --git a/fdroidserver/metadata.py b/fdroidserver/metadata.py index 13a703a6..a5fbe0d1 100644 --- a/fdroidserver/metadata.py +++ b/fdroidserver/metadata.py @@ -126,7 +126,7 @@ class App(dict): self.IssueTracker = '' self.Translation = '' self.Changelog = '' - self.Donate = None + self.Donate = [] self.Liberapay = None self.OpenCollective = None self.Bitcoin = None @@ -188,6 +188,7 @@ fieldtypes = { 'MaintainerNotes': TYPE_MULTILINE, 'Categories': TYPE_LIST, 'AntiFeatures': TYPE_STRINGMAP, + 'Donate': TYPE_LIST, 'RequiresRoot': TYPE_BOOL, 'AllowedAPKSigningKeys': TYPE_LIST, 'Builds': TYPE_BUILD, @@ -1269,6 +1270,11 @@ def _app_to_yaml(app): v = _format_stringmap(app['id'], field, value) if v: cm[field] = v + elif field == 'Donate': + if len(value) == 1: + cm[field] = value[0] + else: + cm[field] = value elif field == 'AllowedAPKSigningKeys': value = [str(i).lower() for i in value] if len(value) == 1: diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 795b212d..23f17364 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -1052,44 +1052,44 @@ def insert_funding_yml_donation_links(apps): if k == 'custom': s = sanitize_funding_yml_entry(v) if s: - app['Donate'] = s + app['Donate'] = [s] break elif k in ('community_bridge', 'lfx_crowdfunding'): s = sanitize_funding_yml_name(v) if s: - app['Donate'] = ( + app['Donate'] = [ f'https://crowdfunding.lfx.linuxfoundation.org/projects/{s}' - ) + ] break elif k == 'github': s = sanitize_funding_yml_name(v) if s: - app['Donate'] = 'https://github.com/sponsors/' + s + app['Donate'] = ['https://github.com/sponsors/' + s] break elif k == 'issuehunt': s = sanitize_funding_yml_name(v) if s: - app['Donate'] = 'https://issuehunt.io/r/' + s + app['Donate'] = ['https://issuehunt.io/r/' + s] break elif k == 'ko_fi': s = sanitize_funding_yml_name(v) if s: - app['Donate'] = 'https://ko-fi.com/' + s + app['Donate'] = ['https://ko-fi.com/' + s] break elif k == 'patreon': s = sanitize_funding_yml_name(v) if s: - app['Donate'] = 'https://patreon.com/' + s + app['Donate'] = ['https://patreon.com/' + s] break elif k == 'buy_me_a_coffee': s = sanitize_funding_yml_name(v) if s: - app['Donate'] = 'https://www.buymeacoffee.com/' + s + app['Donate'] = ['https://www.buymeacoffee.com/' + s] break elif k == 'polar': s = sanitize_funding_yml_name(v) if s: - app['Donate'] = 'https://polar.sh/' + s + app['Donate'] = ['https://polar.sh/' + s] break diff --git a/tests/metadata/dump/app.with.special.build.params.yaml b/tests/metadata/dump/app.with.special.build.params.yaml index fae6c9ae..d0bc4e64 100644 --- a/tests/metadata/dump/app.with.special.build.params.yaml +++ b/tests/metadata/dump/app.with.special.build.params.yaml @@ -346,7 +346,7 @@ Description: 'To configure, go to "Settings => Accounts & Sync => Add Account". Appbrain SDK was removed before building.' Disabled: null -Donate: null +Donate: [] IssueTracker: https://github.com/loadrunner/Facebook-Contact-Sync/issues Liberapay: null License: GPL-3.0-only diff --git a/tests/metadata/dump/com.politedroid.yaml b/tests/metadata/dump/com.politedroid.yaml index 01d55b82..d8c8174c 100644 --- a/tests/metadata/dump/com.politedroid.yaml +++ b/tests/metadata/dump/com.politedroid.yaml @@ -169,7 +169,7 @@ CurrentVersion: '1.5' CurrentVersionCode: 6 Description: Activates silent mode during calendar events. Disabled: null -Donate: null +Donate: [] IssueTracker: https://github.com/miguelvps/PoliteDroid/issues Liberapay: null License: GPL-3.0-only diff --git a/tests/metadata/dump/org.adaway.yaml b/tests/metadata/dump/org.adaway.yaml index 98a249d6..b9bce3d3 100644 --- a/tests/metadata/dump/org.adaway.yaml +++ b/tests/metadata/dump/org.adaway.yaml @@ -1114,7 +1114,8 @@ Description: 'An ad blocker that uses the hosts file. The hosts file read-only.' Disabled: null -Donate: http://sufficientlysecure.org/index.php/adaway +Donate: +- http://sufficientlysecure.org/index.php/adaway IssueTracker: https://github.com/dschuermann/ad-away/issues Liberapay: null License: GPL-3.0-only diff --git a/tests/metadata/dump/org.smssecure.smssecure.yaml b/tests/metadata/dump/org.smssecure.smssecure.yaml index 7410aa68..392aa2fb 100644 --- a/tests/metadata/dump/org.smssecure.smssecure.yaml +++ b/tests/metadata/dump/org.smssecure.smssecure.yaml @@ -384,7 +384,7 @@ Description: 'SMSSecure is an SMS/MMS application that allows you to protect you * Open Source. SMSSecure is Free and Open Source, enabling anyone to verify its security by auditing the code.' Disabled: null -Donate: null +Donate: [] IssueTracker: https://github.com/SMSSecure/SMSSecure/issues Liberapay: null License: GPL-3.0-only diff --git a/tests/metadata/dump/org.videolan.vlc.yaml b/tests/metadata/dump/org.videolan.vlc.yaml index 5ecb108b..a2a4b2b1 100644 --- a/tests/metadata/dump/org.videolan.vlc.yaml +++ b/tests/metadata/dump/org.videolan.vlc.yaml @@ -2605,7 +2605,8 @@ Description: 'Video and audio player that supports a wide range of formats, ' Disabled: null -Donate: http://www.videolan.org/contribute.html#money +Donate: +- http://www.videolan.org/contribute.html#money IssueTracker: http://www.videolan.org/support/index.html#bugs Liberapay: null License: GPL-3.0-only diff --git a/tests/test_lint.py b/tests/test_lint.py index b7d85b7a..a798ccef 100755 --- a/tests/test_lint.py +++ b/tests/test_lint.py @@ -179,13 +179,13 @@ class LintTest(SetUpTearDownMixin, unittest.TestCase): def test_check_regexes_donate(self): app = fdroidserver.metadata.App() - app.Donate = 'https://example.com/donate/' + app.Donate = ['https://example.com/donate/'] for warn in fdroidserver.lint.check_regexes(app): self.fail() def test_check_regexes_donate_both(self): app = fdroidserver.metadata.App() - app.Donate = 'http://bit.ly/givememoney/' + app.Donate = ['http://bit.ly/givememoney/'] warns = list(fdroidserver.lint.check_regexes(app)) for warn in warns: self.assertIn('bit.ly', warn) diff --git a/tests/test_update.py b/tests/test_update.py index 3d5e4fe0..5be348d2 100755 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -1615,7 +1615,7 @@ class UpdateTest(SetUpTearDownMixin, unittest.TestCase): 'CurrentVersion': '', 'CurrentVersionCode': None, 'Disabled': '', - 'Donate': '', + 'Donate': None, 'IssueTracker': '', 'License': '', 'Litecoin': '', @@ -1665,7 +1665,7 @@ class UpdateTest(SetUpTearDownMixin, unittest.TestCase): fdroidserver.update.insert_funding_yml_donation_links(apps) for field in DONATION_FIELDS: self.assertIsNotNone(app.get(field), field) - self.assertEqual('LINK1', app.get('Donate')) + self.assertEqual(['LINK1'], app.get('Donate')) self.assertEqual('USERNAME', app.get('Liberapay')) self.assertEqual('USERNAME', app.get('OpenCollective')) @@ -1686,8 +1686,9 @@ class UpdateTest(SetUpTearDownMixin, unittest.TestCase): apps = {app.id: app} os.mkdir(os.path.join('build', app.id)) fdroidserver.update.insert_funding_yml_donation_links(apps) - for field in DONATION_FIELDS: - self.assertIsNone(app.get(field)) + self.assertEqual(app.get("Donate"), []) + self.assertIsNone(app.get("Liberapay")) + self.assertIsNone(app.get("OpenCollective")) content = textwrap.dedent( """ @@ -1715,8 +1716,10 @@ class UpdateTest(SetUpTearDownMixin, unittest.TestCase): elif 'open_collective' in data: self.assertEqual(data['open_collective'], app.get('OpenCollective')) else: + # 'Donate' metadata for v in data.values(): - self.assertEqual(app.get('Donate', '').split('/')[-1], v) + for donate_option in app.get('Donate', []): + self.assertEqual(donate_option.split('/')[-1], v) def test_insert_funding_yml_donation_links_with_corrupt_file(self): os.chdir(self.testdir) @@ -1736,8 +1739,9 @@ class UpdateTest(SetUpTearDownMixin, unittest.TestCase): ) ) fdroidserver.update.insert_funding_yml_donation_links(apps) - for field in DONATION_FIELDS: - self.assertIsNone(app.get(field)) + self.assertEqual(app.get("Donate"), []) + self.assertIsNone(app.get("Liberapay")) + self.assertIsNone(app.get("OpenCollective")) def test_sanitize_funding_yml(self): with open(basedir / 'funding-usernames.yaml') as fp: