mirror of
				https://github.com/f-droid/fdroidserver.git
				synced 2025-11-04 14:30:30 +03:00 
			
		
		
		
	update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for pointing people to donation links. Since F-Droid also points people to donation links, this parses them to fill out Donate: and OpenCollective:. Specifying those in the metadata file takes precedence over the FUNDING.yml. This follows the same pattern as how `fdroid update` includes Fastlane/Triple-T metadata. This lets the git repo maintain those specific donations links themselves. https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files The test file was generated using: ```python import os, re, yaml found = dict() for root, dirs, files in os.walk('.'): for f in files: if f == 'FUNDING.yml': with open(os.path.join(root, f)) as fp: data = yaml.safe_load(fp) for k, v in data.items(): if k not in found: found[k] = set() if not v: continue if isinstance(v, list): for i in v: found[k].add(i) else: found[k].add(v) with open('gather-funding-names.yaml', 'w') as fp: output = dict() for k, v in found.items(): output[k] = sorted(v) yaml.dump(output, fp, default_flow_style=False) ```
This commit is contained in:
		
							parent
							
								
									8d517d4583
								
							
						
					
					
						commit
						0183592526
					
				
					 5 changed files with 420 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -41,6 +41,10 @@ from fdroidserver.exception import MetaDataException, FDroidException
 | 
			
		|||
srclibs = None
 | 
			
		||||
warnings_action = None
 | 
			
		||||
 | 
			
		||||
# validates usernames based on a loose collection of rules from GitHub, GitLab,
 | 
			
		||||
# Liberapay and issuehunt.  This is mostly to block abuse.
 | 
			
		||||
VALID_USERNAME_REGEX = re.compile(r'^[a-z\d](?:[a-z\d/._-]){0,38}$', re.IGNORECASE)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def warn_or_exception(value, cause=None):
 | 
			
		||||
    '''output warning or Exception depending on -W'''
 | 
			
		||||
| 
						 | 
				
			
			@ -455,7 +459,7 @@ valuetypes = {
 | 
			
		|||
                   ['LiberapayID']),
 | 
			
		||||
 | 
			
		||||
    FieldValidator("Open Collective",
 | 
			
		||||
                   r'^[0-9a-zA-Z_-]+$',
 | 
			
		||||
                   VALID_USERNAME_REGEX,
 | 
			
		||||
                   ['OpenCollective']),
 | 
			
		||||
 | 
			
		||||
    FieldValidator("HTTP link",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,10 +31,15 @@ import zipfile
 | 
			
		|||
import hashlib
 | 
			
		||||
import json
 | 
			
		||||
import time
 | 
			
		||||
import yaml
 | 
			
		||||
import copy
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from argparse import ArgumentParser
 | 
			
		||||
from base64 import urlsafe_b64encode
 | 
			
		||||
try:
 | 
			
		||||
    from yaml import CSafeLoader as SafeLoader
 | 
			
		||||
except ImportError:
 | 
			
		||||
    from yaml import SafeLoader
 | 
			
		||||
 | 
			
		||||
import collections
 | 
			
		||||
from binascii import hexlify
 | 
			
		||||
| 
						 | 
				
			
			@ -854,6 +859,118 @@ def _get_base_hash_extension(f):
 | 
			
		|||
    return base, None, extension
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def sanitize_funding_yml_entry(entry):
 | 
			
		||||
    """FUNDING.yml comes from upstream repos, entries must be sanitized"""
 | 
			
		||||
    if type(entry) not in (bytes, int, float, list, str):
 | 
			
		||||
        return
 | 
			
		||||
    if isinstance(entry, bytes):
 | 
			
		||||
        entry = entry.decode()
 | 
			
		||||
    elif isinstance(entry, list):
 | 
			
		||||
        if entry:
 | 
			
		||||
            entry = entry[0]
 | 
			
		||||
        else:
 | 
			
		||||
            return
 | 
			
		||||
    try:
 | 
			
		||||
        entry = str(entry)
 | 
			
		||||
    except (TypeError, ValueError):
 | 
			
		||||
        return
 | 
			
		||||
    if len(entry) > 2048:
 | 
			
		||||
        logging.warning(_('Ignoring FUNDING.yml entry longer than 2048: %s') % entry[:2048])
 | 
			
		||||
        return
 | 
			
		||||
    if '\n' in entry:
 | 
			
		||||
        return
 | 
			
		||||
    return entry.strip()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def sanitize_funding_yml_name(name):
 | 
			
		||||
    """Sanitize usernames that come from FUNDING.yml"""
 | 
			
		||||
    entry = sanitize_funding_yml_entry(name)
 | 
			
		||||
    if entry:
 | 
			
		||||
        m = metadata.VALID_USERNAME_REGEX.match(entry)
 | 
			
		||||
        if m:
 | 
			
		||||
            return m.group()
 | 
			
		||||
    return
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def insert_funding_yml_donation_links(apps):
 | 
			
		||||
    """include donation links from FUNDING.yml in app's source repo
 | 
			
		||||
 | 
			
		||||
    GitHub made a standard file format for declaring donation
 | 
			
		||||
    links. This parses that format from upstream repos to include in
 | 
			
		||||
    metadata here.  GitHub supports mostly proprietary services, so
 | 
			
		||||
    this logic adds proprietary services only as Donate: links.
 | 
			
		||||
 | 
			
		||||
    FUNDING.yml can be either in the root of the project, or in the
 | 
			
		||||
    ".github" subdir.
 | 
			
		||||
 | 
			
		||||
    https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    if not os.path.isdir('build'):
 | 
			
		||||
        return  # nothing to do
 | 
			
		||||
    for packageName, app in apps.items():
 | 
			
		||||
        sourcedir = os.path.join('build', packageName)
 | 
			
		||||
        if not os.path.isdir(sourcedir):
 | 
			
		||||
            continue
 | 
			
		||||
        for f in ([os.path.join(sourcedir, 'FUNDING.yml'), ]
 | 
			
		||||
                  + glob.glob(os.path.join(sourcedir, '.github', 'FUNDING.yml'))):
 | 
			
		||||
            if not os.path.isfile(f):
 | 
			
		||||
                continue
 | 
			
		||||
            data = None
 | 
			
		||||
            try:
 | 
			
		||||
                with open(f) as fp:
 | 
			
		||||
                    data = yaml.load(fp, Loader=SafeLoader)
 | 
			
		||||
            except yaml.YAMLError as e:
 | 
			
		||||
                logging.error(_('Found bad funding file "{path}" for "{name}":')
 | 
			
		||||
                              .format(path=f, name=packageName))
 | 
			
		||||
                logging.error(e)
 | 
			
		||||
            if not data or type(data) != dict:
 | 
			
		||||
                continue
 | 
			
		||||
            if not app.get('OpenCollective') and 'open_collective' in data:
 | 
			
		||||
                s = sanitize_funding_yml_name(data['open_collective'])
 | 
			
		||||
                if s:
 | 
			
		||||
                    app['OpenCollective'] = s
 | 
			
		||||
            if not app.get('Donate'):
 | 
			
		||||
                del(data['liberapay'])
 | 
			
		||||
                del(data['open_collective'])
 | 
			
		||||
                # this tuple provides a preference ordering
 | 
			
		||||
                for k in ('custom', 'github', 'patreon', 'community_bridge', 'ko_fi', 'issuehunt'):
 | 
			
		||||
                    v = data.get(k)
 | 
			
		||||
                    if not v:
 | 
			
		||||
                        continue
 | 
			
		||||
                    if k == 'custom':
 | 
			
		||||
                        s = sanitize_funding_yml_entry(v)
 | 
			
		||||
                        if s:
 | 
			
		||||
                            app['Donate'] = s
 | 
			
		||||
                            break
 | 
			
		||||
                    elif k == 'community_bridge':
 | 
			
		||||
                        s = sanitize_funding_yml_name(v)
 | 
			
		||||
                        if s:
 | 
			
		||||
                            app['Donate'] = 'https://funding.communitybridge.org/projects/' + s
 | 
			
		||||
                            break
 | 
			
		||||
                    elif k == 'github':
 | 
			
		||||
                        s = sanitize_funding_yml_name(v)
 | 
			
		||||
                        if 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
 | 
			
		||||
                            break
 | 
			
		||||
                    elif k == 'ko_fi':
 | 
			
		||||
                        s = sanitize_funding_yml_name(v)
 | 
			
		||||
                        if 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
 | 
			
		||||
                            break
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def copy_triple_t_store_metadata(apps):
 | 
			
		||||
    """Include store metadata from the app's source repo
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2179,6 +2296,7 @@ def main():
 | 
			
		|||
                else:
 | 
			
		||||
                    logging.warning(msg + '\n\t' + _('Use `fdroid update -c` to create it.'))
 | 
			
		||||
 | 
			
		||||
    insert_funding_yml_donation_links(apps)
 | 
			
		||||
    copy_triple_t_store_metadata(apps)
 | 
			
		||||
    insert_obbs(repodirs[0], apps, apks)
 | 
			
		||||
    insert_localized_app_metadata(apps)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										197
									
								
								tests/funding-usernames.yaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								tests/funding-usernames.yaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,197 @@
 | 
			
		|||
bad:
 | 
			
		||||
  - "Robert'); DROP TABLE Students; --"
 | 
			
		||||
  - ''
 | 
			
		||||
  - -a-b
 | 
			
		||||
  - '1234567890123456789012345678901234567890'
 | 
			
		||||
  - ~derp@darp---++asdf
 | 
			
		||||
  - foo@bar.com
 | 
			
		||||
  - me++
 | 
			
		||||
  - --me
 | 
			
		||||
bitcoin:
 | 
			
		||||
  - 3Lbz4vdt15Fsa4wVD3Yk8uGf6ugKKY4zSc
 | 
			
		||||
community_bridge: []
 | 
			
		||||
custom:
 | 
			
		||||
  - bc1qvll2mp5ndwd4sgycu4ad2ken4clhjac7mdlcaj
 | 
			
		||||
  - http://www.roguetemple.com/z/donate.php
 | 
			
		||||
  - https://donate.openfoodfacts.org
 | 
			
		||||
  - https://email.faircode.eu/donate/
 | 
			
		||||
  - https://etchdroid.depau.eu/donate/
 | 
			
		||||
  - https://f-droid.org/about/
 | 
			
		||||
  - https://flattr.com/github/bk138
 | 
			
		||||
  - https://gultsch.de/donate.html
 | 
			
		||||
  - https://jahir.dev/donate
 | 
			
		||||
  - https://kodi.tv/contribute/donate
 | 
			
		||||
  - https://link.xbrowsersync.org/cryptos
 | 
			
		||||
  - https://manyver.se/donate
 | 
			
		||||
  - https://paypal.me/DanielQuahShaoHian
 | 
			
		||||
  - https://paypal.me/deletescape
 | 
			
		||||
  - https://paypal.me/freaktechnik
 | 
			
		||||
  - https://paypal.me/hpoul
 | 
			
		||||
  - https://paypal.me/imkosh
 | 
			
		||||
  - https://paypal.me/paphonb
 | 
			
		||||
  - https://paypal.me/vocabletrainer
 | 
			
		||||
  - https://pendulums.io/donation.html
 | 
			
		||||
  - https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
 | 
			
		||||
  - https://play.google.com/store/apps/details?id=eu.faircode.email
 | 
			
		||||
  - https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/donate.png
 | 
			
		||||
  - https://raw.githubusercontent.com/CarGuo/GSYGithubAppFlutter/master/thanks.jpg
 | 
			
		||||
  - https://raw.githubusercontent.com/GanZhiXiong/GZXTaoBaoAppFlutter/blob/master/preview_images/thanks.png
 | 
			
		||||
  - https://seriesgui.de/whypay
 | 
			
		||||
  - https://transportr.app/donate/
 | 
			
		||||
  - https://www.bountysource.com/teams/nextcloud/issues?tracker_ids=38838206
 | 
			
		||||
  - https://www.donationalerts.com/r/blidingmage835
 | 
			
		||||
  - https://www.hellotux.com/f-droid
 | 
			
		||||
  - https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8UH5MBVYM3J36
 | 
			
		||||
  - https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=E2FCXCT6837GL
 | 
			
		||||
  - https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FMLNN8GXZKJEE
 | 
			
		||||
  - https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=K7HVLE6J7SXXA
 | 
			
		||||
  - https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZD39ZE7MGEGBL&source=url
 | 
			
		||||
  - https://www.paypal.me/SimpleMobileTools
 | 
			
		||||
  - https://www.paypal.me/TheAlphamerc/
 | 
			
		||||
  - https://www.paypal.me/avirias
 | 
			
		||||
  - https://www.paypal.me/btimofeev
 | 
			
		||||
  - https://www.paypal.me/enricocid
 | 
			
		||||
  - https://www.paypal.me/gsnathan
 | 
			
		||||
  - https://www.paypal.me/nikita36078
 | 
			
		||||
  - https://www.paypal.me/sahdeep
 | 
			
		||||
  - https://www.paypal.me/saulhenriquez
 | 
			
		||||
  - https://www.simplemobiletools.com/donate
 | 
			
		||||
  - https://www.youtube.com/watch?v=ZmrNc1ZhBkQ
 | 
			
		||||
  - paypal.me/amangautam1
 | 
			
		||||
  - paypal.me/pools/c/8lCZfNnU0u
 | 
			
		||||
  - paypal.me/psoffritti
 | 
			
		||||
github:
 | 
			
		||||
  - 00-Evan
 | 
			
		||||
  - adrcotfas
 | 
			
		||||
  - afollestad
 | 
			
		||||
  - ar-
 | 
			
		||||
  - BarnabyShearer
 | 
			
		||||
  - CarGuo
 | 
			
		||||
  - cketti
 | 
			
		||||
  - eighthave
 | 
			
		||||
  - emansih
 | 
			
		||||
  - GanZhiXiong
 | 
			
		||||
  - gpeal
 | 
			
		||||
  - hpoul
 | 
			
		||||
  - i--
 | 
			
		||||
  - inorichi
 | 
			
		||||
  - inputmice
 | 
			
		||||
  - jahirfiquitiva
 | 
			
		||||
  - johnjohndoe
 | 
			
		||||
  - kaloudis
 | 
			
		||||
  - kiwix
 | 
			
		||||
  - ligi
 | 
			
		||||
  - M66B
 | 
			
		||||
  - mikepenz
 | 
			
		||||
  - Mygod
 | 
			
		||||
  - paroj
 | 
			
		||||
  - PerfectSlayer
 | 
			
		||||
  - sschueller
 | 
			
		||||
  - tateisu
 | 
			
		||||
  - tibbi
 | 
			
		||||
  - westnordost
 | 
			
		||||
  - x1unix
 | 
			
		||||
  - xn--nding-jua
 | 
			
		||||
  - zenorogue
 | 
			
		||||
issuehunt:
 | 
			
		||||
  - bk138/multivnc
 | 
			
		||||
ko_fi:
 | 
			
		||||
  - afollestad
 | 
			
		||||
  - fennifith
 | 
			
		||||
  - inorichi
 | 
			
		||||
  - mastalab
 | 
			
		||||
  - psoffritti
 | 
			
		||||
liberapay:
 | 
			
		||||
  - ActivityDiary
 | 
			
		||||
  - AndStatus
 | 
			
		||||
  - BM835
 | 
			
		||||
  - Briar
 | 
			
		||||
  - DAVx5
 | 
			
		||||
  - F-Droid-Data
 | 
			
		||||
  - Feeel
 | 
			
		||||
  - Fruit-Radar-Development
 | 
			
		||||
  - Gadgetbridge
 | 
			
		||||
  - GuardianProject
 | 
			
		||||
  - Hocuri
 | 
			
		||||
  - KOReader
 | 
			
		||||
  - Kanedias
 | 
			
		||||
  - Kunzisoft
 | 
			
		||||
  - MaxK
 | 
			
		||||
  - NovaVideoPlayer
 | 
			
		||||
  - Phie
 | 
			
		||||
  - Rudloff
 | 
			
		||||
  - Schoumi
 | 
			
		||||
  - Syncthing-Fork
 | 
			
		||||
  - TeamNewPipe
 | 
			
		||||
  - Telegram-FOSS
 | 
			
		||||
  - Transportr
 | 
			
		||||
  - Varlorg
 | 
			
		||||
  - Wesnoth
 | 
			
		||||
  - ZiiS
 | 
			
		||||
  - ar-
 | 
			
		||||
  - bk138
 | 
			
		||||
  - btimofeev
 | 
			
		||||
  - bubblineyuri
 | 
			
		||||
  - dennis.guse
 | 
			
		||||
  - developerfromjokela
 | 
			
		||||
  - devgianlu
 | 
			
		||||
  - eneiluj
 | 
			
		||||
  - experiment322
 | 
			
		||||
  - fdossena
 | 
			
		||||
  - fennifith
 | 
			
		||||
  - freaktechnik
 | 
			
		||||
  - gsantner
 | 
			
		||||
  - hisname
 | 
			
		||||
  - hsn6
 | 
			
		||||
  - iNPUTmice
 | 
			
		||||
  - inputmice
 | 
			
		||||
  - k9mail
 | 
			
		||||
  - matrixdotorg
 | 
			
		||||
  - mmarif
 | 
			
		||||
  - moezbhatti
 | 
			
		||||
  - proninyaroslav
 | 
			
		||||
  - quite
 | 
			
		||||
  - renyuneyun
 | 
			
		||||
  - rocketnine.space
 | 
			
		||||
  - sanskritbscs
 | 
			
		||||
  - sschueller
 | 
			
		||||
  - sschueller/donate
 | 
			
		||||
  - stefan-niedermann
 | 
			
		||||
  - tasks
 | 
			
		||||
  - teamkodi
 | 
			
		||||
  - thermatk
 | 
			
		||||
  - tom79
 | 
			
		||||
  - wallabag
 | 
			
		||||
  - westnordost
 | 
			
		||||
  - whyorean
 | 
			
		||||
  - wilko
 | 
			
		||||
  - xbrowsersync
 | 
			
		||||
  - yeriomin
 | 
			
		||||
  - zeh
 | 
			
		||||
open_collective:
 | 
			
		||||
  - avirias
 | 
			
		||||
  - curl
 | 
			
		||||
  - libsodium
 | 
			
		||||
  - manyverse
 | 
			
		||||
  - mastalab
 | 
			
		||||
  - tusky
 | 
			
		||||
otechie: []
 | 
			
		||||
patreon:
 | 
			
		||||
  - BaldPhone
 | 
			
		||||
  - Bm835
 | 
			
		||||
  - FastHub
 | 
			
		||||
  - Teamkodi
 | 
			
		||||
  - andrestaltz
 | 
			
		||||
  - bk138
 | 
			
		||||
  - depau
 | 
			
		||||
  - iamSahdeep
 | 
			
		||||
  - ligi
 | 
			
		||||
  - ogre1
 | 
			
		||||
  - orhunp
 | 
			
		||||
  - tiborkaputa
 | 
			
		||||
  - tom79
 | 
			
		||||
  - westnordost
 | 
			
		||||
  - xbrowsersync
 | 
			
		||||
  - yairm210
 | 
			
		||||
  - zenorogue
 | 
			
		||||
tidelift: []
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +100,21 @@ class MetadataTest(unittest.TestCase):
 | 
			
		|||
        self.assertRaises(fdroidserver.exception.MetaDataException, validator.check,
 | 
			
		||||
                          'tb1qw5r8drrejxrrg4y5rrrrrraryrrrrwrkxrjrsx', 'fake.app.id')
 | 
			
		||||
 | 
			
		||||
    def test_valid_funding_yml_regex(self):
 | 
			
		||||
        """Check the regex can find all the cases"""
 | 
			
		||||
        with open(os.path.join(self.basedir, 'funding-usernames.yaml')) as fp:
 | 
			
		||||
            data = yaml.safe_load(fp)
 | 
			
		||||
 | 
			
		||||
        for k, entries in data.items():
 | 
			
		||||
            for entry in entries:
 | 
			
		||||
                m = fdroidserver.metadata.VALID_USERNAME_REGEX.match(entry)
 | 
			
		||||
                if k == 'custom':
 | 
			
		||||
                    pass
 | 
			
		||||
                elif k == 'bad':
 | 
			
		||||
                    self.assertIsNone(m, 'this is an invalid %s username: {%s}' % (k, entry))
 | 
			
		||||
                else:
 | 
			
		||||
                    self.assertIsNotNone(m, 'this is a valid %s username: {%s}' % (k, entry))
 | 
			
		||||
 | 
			
		||||
    def test_read_metadata(self):
 | 
			
		||||
 | 
			
		||||
        def _build_yaml_representer(dumper, data):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@ import inspect
 | 
			
		|||
import logging
 | 
			
		||||
import optparse
 | 
			
		||||
import os
 | 
			
		||||
import random
 | 
			
		||||
import shutil
 | 
			
		||||
import subprocess
 | 
			
		||||
import sys
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +34,12 @@ import fdroidserver.update
 | 
			
		|||
from fdroidserver.common import FDroidPopen
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DONATION_FIELDS = (
 | 
			
		||||
    'Donate',
 | 
			
		||||
    'OpenCollective',
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UpdateTest(unittest.TestCase):
 | 
			
		||||
    '''fdroid update'''
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -972,6 +979,84 @@ class UpdateTest(unittest.TestCase):
 | 
			
		|||
                                      'VercodeOperation': '',
 | 
			
		||||
                                      'WebSite': ''})
 | 
			
		||||
 | 
			
		||||
    def test_insert_funding_yml_donation_links(self):
 | 
			
		||||
        testdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir)
 | 
			
		||||
        os.chdir(testdir)
 | 
			
		||||
        os.mkdir('build')
 | 
			
		||||
        content = textwrap.dedent("""
 | 
			
		||||
            community_bridge: ''
 | 
			
		||||
            custom: [LINK1, LINK2]
 | 
			
		||||
            github: USERNAME
 | 
			
		||||
            issuehunt: USERNAME
 | 
			
		||||
            ko_fi: USERNAME
 | 
			
		||||
            liberapay: USERNAME
 | 
			
		||||
            open_collective: USERNAME
 | 
			
		||||
            otechie: USERNAME
 | 
			
		||||
            patreon: USERNAME
 | 
			
		||||
        """)
 | 
			
		||||
        app = fdroidserver.metadata.App()
 | 
			
		||||
        app.id = 'fake.app.id'
 | 
			
		||||
        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.assertFalse(app.get(field))
 | 
			
		||||
        with open(os.path.join('build', app.id, 'FUNDING.yml'), 'w') as fp:
 | 
			
		||||
            fp.write(content)
 | 
			
		||||
 | 
			
		||||
        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('USERNAME', app.get('OpenCollective'))
 | 
			
		||||
 | 
			
		||||
        app['Donate'] = 'keepme'
 | 
			
		||||
        app['OpenCollective'] = 'keepme'
 | 
			
		||||
        fdroidserver.update.insert_funding_yml_donation_links(apps)
 | 
			
		||||
        for field in DONATION_FIELDS:
 | 
			
		||||
            self.assertEqual('keepme', app.get(field))
 | 
			
		||||
 | 
			
		||||
    def test_insert_funding_yml_donation_links_with_corrupt_file(self):
 | 
			
		||||
        testdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir)
 | 
			
		||||
        os.chdir(testdir)
 | 
			
		||||
        os.mkdir('build')
 | 
			
		||||
        app = fdroidserver.metadata.App()
 | 
			
		||||
        app.id = 'fake.app.id'
 | 
			
		||||
        apps = {app.id: app}
 | 
			
		||||
        os.mkdir(os.path.join('build', app.id))
 | 
			
		||||
        with open(os.path.join('build', app.id, 'FUNDING.yml'), 'w') as fp:
 | 
			
		||||
            fp.write(textwrap.dedent("""
 | 
			
		||||
                opencollective: foo
 | 
			
		||||
                custom: []
 | 
			
		||||
                liberapay: :
 | 
			
		||||
            """))
 | 
			
		||||
        fdroidserver.update.insert_funding_yml_donation_links(apps)
 | 
			
		||||
        for field in DONATION_FIELDS:
 | 
			
		||||
            self.assertIsNone(app.get(field))
 | 
			
		||||
 | 
			
		||||
    def test_sanitize_funding_yml(self):
 | 
			
		||||
        with open(os.path.join(self.basedir, 'funding-usernames.yaml')) as fp:
 | 
			
		||||
            data = yaml.safe_load(fp)
 | 
			
		||||
        for k, entries in data.items():
 | 
			
		||||
            for entry in entries:
 | 
			
		||||
                if k in 'custom':
 | 
			
		||||
                    m = fdroidserver.update.sanitize_funding_yml_entry(entry)
 | 
			
		||||
                else:
 | 
			
		||||
                    m = fdroidserver.update.sanitize_funding_yml_name(entry)
 | 
			
		||||
                if k == 'bad':
 | 
			
		||||
                    self.assertIsNone(m)
 | 
			
		||||
                else:
 | 
			
		||||
                    self.assertIsNotNone(m)
 | 
			
		||||
        self.assertIsNone(fdroidserver.update.sanitize_funding_yml_entry('foo\nbar'))
 | 
			
		||||
        self.assertIsNone(fdroidserver.update.sanitize_funding_yml_entry(
 | 
			
		||||
            ''.join(chr(random.randint(65, 90)) for _ in range(2049))))
 | 
			
		||||
 | 
			
		||||
        # not recommended but valid entries
 | 
			
		||||
        self.assertIsNotNone(fdroidserver.update.sanitize_funding_yml_entry(12345))
 | 
			
		||||
        self.assertIsNotNone(fdroidserver.update.sanitize_funding_yml_entry(5.0))
 | 
			
		||||
        self.assertIsNotNone(fdroidserver.update.sanitize_funding_yml_entry(' WhyIncludeWhitespace '))
 | 
			
		||||
        self.assertIsNotNone(fdroidserver.update.sanitize_funding_yml_entry(['first', 'second']))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    os.chdir(os.path.dirname(__file__))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue