Merge branch 'lint-AntiFeatures-from-config' into 'master'

lint: Anti-Features validator uses names from config / plus tests, cleanups, and minor bugfixes

See merge request fdroid/fdroidserver!1352
This commit is contained in:
Michael Pöhn 2023-05-11 14:20:15 +00:00
commit 1b765d11e7
35 changed files with 1336 additions and 161 deletions

6
.gitignore vendored
View file

@ -7,11 +7,11 @@ TAGS
.ropeproject/
# files generated by build
build/
dist/
/build/
/dist/
env/
ENV/
fdroidserver.egg-info/
/fdroidserver.egg-info/
pylint.parseable
/.testfiles/
README.rst

View file

@ -41,7 +41,7 @@ metadata_v0:
image: registry.gitlab.com/fdroid/fdroidserver:buildserver
variables:
GIT_DEPTH: 1000
RELEASE_COMMIT_ID: 58cfce106b6d68dc8ebde7842cf01225f5adfd1b # 2.2b
RELEASE_COMMIT_ID: 0124b9dde99f9cab19c034cbc7d8cc6005a99b48 # 2.3a0
script:
- git fetch https://gitlab.com/fdroid/fdroidserver.git $RELEASE_COMMIT_ID
- cd tests
@ -125,6 +125,10 @@ ubuntu_lts_ppa:
- apt-get update
- apt-get dist-upgrade
- apt-get install --install-recommends dexdump fdroidserver git jq python3-setuptools sdkmanager
# Test things work with a default branch other than 'master'
- git config --global init.defaultBranch thisisnotmasterormain
- cd tests
- ./run-tests
@ -584,12 +588,12 @@ docker:
RELEASE_IMAGE: $CI_REGISTRY_IMAGE:buildserver
script:
# git ref names can contain many chars that are not allowed in docker tags
- export TEST_IMAGE=$CI_REGISTRY_IMAGE:$(printf $CI_BUILD_REF_NAME | sed 's,[^a-zA-Z0-9_.-],_,g')
- export TEST_IMAGE=$CI_REGISTRY_IMAGE:$(printf $CI_COMMIT_REF_NAME | sed 's,[^a-zA-Z0-9_.-],_,g')
- cd buildserver
- docker build -t $TEST_IMAGE --build-arg GIT_REV_PARSE_HEAD=$(git rev-parse HEAD) .
- docker tag $TEST_IMAGE $RELEASE_IMAGE
- docker tag $TEST_IMAGE ${RELEASE_IMAGE}-bullseye
- echo $CI_BUILD_TOKEN | docker login -u gitlab-ci-token --password-stdin registry.gitlab.com
- echo $CI_JOB_TOKEN | docker login -u gitlab-ci-token --password-stdin registry.gitlab.com
# This avoids filling up gitlab.com free tier accounts with unused docker images.
- if test -z "$FDROID_PUSH_DOCKER_IMAGE"; then
echo "Skipping docker push to save quota on your gitlab namespace.";

View file

@ -545,6 +545,22 @@ include tests/check-fdroid-apk
include tests/checkupdates.TestCase
include tests/common.TestCase
include tests/config.py
include tests/config/antiFeatures.yml
include tests/config/de/antiFeatures.yml
include tests/config/fa/antiFeatures.yml
include tests/config/ic_antifeature_ads.xml
include tests/config/ic_antifeature_disabledalgorithm.xml
include tests/config/ic_antifeature_knownvuln.xml
include tests/config/ic_antifeature_nonfreeadd.xml
include tests/config/ic_antifeature_nonfreeassets.xml
include tests/config/ic_antifeature_nonfreedep.xml
include tests/config/ic_antifeature_nonfreenet.xml
include tests/config/ic_antifeature_nosourcesince.xml
include tests/config/ic_antifeature_nsfw.xml
include tests/config/ic_antifeature_tracking.xml
include tests/config/ic_antifeature_upstreamnonfree.xml
include tests/config/ro/antiFeatures.yml
include tests/config/zh-rCN/antiFeatures.yml
include tests/corrupt-featureGraphic.png
include tests/deploy.TestCase
include tests/dummy-keystore.jks

View file

@ -65,7 +65,7 @@ def make_binary_transparency_log(
else:
if not os.path.exists(btrepo):
os.mkdir(btrepo)
gitrepo = git.Repo.init(btrepo)
gitrepo = git.Repo.init(btrepo, initial_branch=deploy.GIT_BRANCH)
if not url:
url = common.config['repo_url'].rstrip('/')

View file

@ -77,6 +77,9 @@ from . import apksigcopier, common
# The path to this fdroidserver distribution
FDROID_PATH = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
# There needs to be a default, and this is the most common for software.
DEFAULT_LOCALE = 'en-US'
# this is the build-tools version, aapt has a separate version that
# has to be manually set in test_aapt_version()
MINIMUM_AAPT_BUILD_TOOLS_VERSION = '26.0.0'
@ -487,6 +490,52 @@ def read_config(opts=None):
return config
def file_entry(filename, hash_value=None):
meta = {}
meta["name"] = "/" + filename.split("/", 1)[1]
meta["sha256"] = hash_value or common.sha256sum(filename)
meta["size"] = os.stat(filename).st_size
return meta
def load_localized_config(name, repodir):
"""Load localized config files and put them into internal dict format.
This will maintain the order as came from the data files, e.g
YAML. The locale comes from unsorted paths on the filesystem, so
that is separately sorted.
"""
ret = dict()
for f in Path().glob("config/**/{name}.yml".format(name=name)):
locale = f.parts[1]
if len(f.parts) == 2:
locale = DEFAULT_LOCALE
with open(f, encoding="utf-8") as fp:
elem = yaml.safe_load(fp)
for afname, field_dict in elem.items():
if afname not in ret:
ret[afname] = dict()
for key, value in field_dict.items():
if key not in ret[afname]:
ret[afname][key] = dict()
if key == "icon":
icons_dir = os.path.join(repodir, 'icons')
if not os.path.exists(icons_dir):
os.mkdir(icons_dir)
shutil.copy(os.path.join("config", value), icons_dir)
ret[afname][key][locale] = file_entry(
os.path.join(icons_dir, value)
)
else:
ret[afname][key][locale] = value
for elem in ret.values():
for afname in elem:
elem[afname] = {locale: v for locale, v in sorted(elem[afname].items())}
return ret
def parse_human_readable_size(size):
units = {
'b': 1,
@ -3866,7 +3915,7 @@ def get_app_display_name(app):
if app.get('Name'):
return app['Name']
if app.get('localized'):
localized = app['localized'].get('en-US')
localized = app['localized'].get(DEFAULT_LOCALE)
if not localized:
for v in app['localized'].values():
localized = v

View file

@ -39,6 +39,8 @@ config = None
options = None
start_timestamp = time.gmtime()
GIT_BRANCH = 'master'
BINARY_TRANSPARENCY_DIR = 'binary_transparency'
AUTO_S3CFG = '.fdroid-deploy-s3cfg'
@ -407,7 +409,7 @@ def update_servergitmirrors(servergitmirrors, repo_section):
elif 'identity_file' in config:
ssh_cmd += ' -oIdentitiesOnly=yes -i "%s"' % config['identity_file']
repo = git.Repo.init(git_mirror_path)
repo = git.Repo.init(git_mirror_path, initial_branch=GIT_BRANCH)
enabled_remotes = []
for remote_url in servergitmirrors:
@ -480,7 +482,9 @@ def update_servergitmirrors(servergitmirrors, repo_section):
logging.debug(_('Pushing to {url}').format(url=remote.url))
with repo.git.custom_environment(GIT_SSH_COMMAND=ssh_cmd):
pushinfos = remote.push('master', force=True, set_upstream=True, progress=progress)
pushinfos = remote.push(
GIT_BRANCH, force=True, set_upstream=True, progress=progress
)
for pushinfo in pushinfos:
if pushinfo.flags & (git.remote.PushInfo.ERROR
| git.remote.PushInfo.REJECTED
@ -691,7 +695,7 @@ def push_binary_transparency(git_repo_path, git_remote):
remote_path = os.path.abspath(git_repo_path)
if not os.path.isdir(os.path.join(git_remote, '.git')):
os.makedirs(git_remote, exist_ok=True)
thumbdriverepo = git.Repo.init(git_remote)
thumbdriverepo = git.Repo.init(git_remote, initial_branch=GIT_BRANCH)
local = thumbdriverepo.create_remote('local', remote_path)
else:
thumbdriverepo = git.Repo(git_remote)
@ -702,7 +706,7 @@ def push_binary_transparency(git_repo_path, git_remote):
local.set_url(remote_path)
else:
local = thumbdriverepo.create_remote('local', remote_path)
local.pull('master')
local.pull(GIT_BRANCH)
else:
# from online machine to remote on a server on the internet
gitrepo = git.Repo(git_repo_path)
@ -713,7 +717,7 @@ def push_binary_transparency(git_repo_path, git_remote):
origin.set_url(git_remote)
else:
origin = gitrepo.create_remote('origin', git_remote)
origin.push('master')
origin.push(GIT_BRANCH)
def main():

View file

@ -29,7 +29,6 @@ import re
import shutil
import tempfile
import urllib.parse
import yaml
import zipfile
import calendar
import qrcode
@ -43,7 +42,7 @@ from . import common
from . import metadata
from . import net
from . import signindex
from fdroidserver.common import FDroidPopen, FDroidPopenBytes, load_stats_fdroid_signing_key_fingerprints
from fdroidserver.common import DEFAULT_LOCALE, FDroidPopen, FDroidPopenBytes, load_stats_fdroid_signing_key_fingerprints
from fdroidserver.exception import FDroidException, VerificationException
@ -472,37 +471,6 @@ def dict_diff(source, target):
return result
def file_entry(filename, hash_value=None):
meta = {}
meta["name"] = "/" + filename.split("/", 1)[1]
meta["sha256"] = hash_value or common.sha256sum(filename)
meta["size"] = os.stat(filename).st_size
return meta
def load_locale(name, repodir):
lst = {}
for yml in Path().glob("config/**/{name}.yml".format(name=name)):
locale = yml.parts[1]
if len(yml.parts) == 2:
locale = "en-US"
with open(yml, encoding="utf-8") as fp:
elem = yaml.safe_load(fp)
for akey, avalue in elem.items():
if akey not in lst:
lst[akey] = {}
for key, value in avalue.items():
if key not in lst[akey]:
lst[akey][key] = {}
if key == "icon":
shutil.copy(os.path.join("config", value), os.path.join(repodir, "icons"))
lst[akey][key][locale] = file_entry(os.path.join(repodir, "icons", value))
else:
lst[akey][key][locale] = value
return lst
def convert_datetime(obj):
if isinstance(obj, datetime):
# Java prefers milliseconds
@ -550,14 +518,14 @@ def package_metadata(app, repodir):
):
element_new = element[:1].lower() + element[1:]
if element in app and app[element]:
meta[element_new] = {"en-US": convert_datetime(app[element])}
meta[element_new] = {DEFAULT_LOCALE: convert_datetime(app[element])}
elif "localized" in app:
localized = {k: v[element_new] for k, v in app["localized"].items() if element_new in v}
if localized:
meta[element_new] = localized
if "name" not in meta and app["AutoName"]:
meta["name"] = {"en-US": app["AutoName"]}
meta["name"] = {DEFAULT_LOCALE: app["AutoName"]}
# fdroidserver/metadata.py App default
if meta["license"] == "Unknown":
@ -568,7 +536,8 @@ def package_metadata(app, repodir):
# TODO handle different resolutions
if app.get("icon"):
meta["icon"] = {"en-US": file_entry(os.path.join(repodir, "icons", app["icon"]))}
icon_path = os.path.join(repodir, "icons", app["icon"])
meta["icon"] = {DEFAULT_LOCALE: common.file_entry(icon_path)}
if "iconv2" in app:
meta["icon"] = app["iconv2"]
@ -594,16 +563,16 @@ def convert_version(version, app, repodir):
ver["file"]["ipfsCIDv1"] = ipfsCIDv1
if "srcname" in version:
ver["src"] = file_entry(os.path.join(repodir, version["srcname"]))
ver["src"] = common.file_entry(os.path.join(repodir, version["srcname"]))
if "obbMainFile" in version:
ver["obbMainFile"] = file_entry(
ver["obbMainFile"] = common.file_entry(
os.path.join(repodir, version["obbMainFile"]),
version["obbMainFileSha256"],
)
if "obbPatchFile" in version:
ver["obbPatchFile"] = file_entry(
ver["obbPatchFile"] = common.file_entry(
os.path.join(repodir, version["obbPatchFile"]),
version["obbPatchFileSha256"],
)
@ -684,11 +653,13 @@ def convert_version(version, app, repodir):
def v2_repo(repodict, repodir, archive):
repo = {}
repo["name"] = {"en-US": repodict["name"]}
repo["description"] = {"en-US": repodict["description"]}
repo["icon"] = {"en-US": file_entry("{}/icons/{}".format(repodir, repodict["icon"]))}
repo["name"] = {DEFAULT_LOCALE: repodict["name"]}
repo["description"] = {DEFAULT_LOCALE: repodict["description"]}
repo["icon"] = {
DEFAULT_LOCALE: common.file_entry("%s/icons/%s" % (repodir, repodict["icon"]))
}
config = load_locale("config", repodir)
config = common.load_localized_config("config", repodir)
if config:
repo["name"] = config["archive" if archive else "repo"]["name"]
repo["description"] = config["archive" if archive else "repo"]["description"]
@ -702,15 +673,15 @@ def v2_repo(repodict, repodir, archive):
repo["timestamp"] = repodict["timestamp"]
antiFeatures = load_locale("antiFeatures", repodir)
antiFeatures = common.load_localized_config("antiFeatures", repodir)
if antiFeatures:
repo["antiFeatures"] = antiFeatures
categories = load_locale("categories", repodir)
categories = common.load_localized_config("categories", repodir)
if categories:
repo["categories"] = categories
channels = load_locale("channels", repodir)
channels = common.load_localized_config("channels", repodir)
if channels:
repo["releaseChannels"] = channels
@ -735,7 +706,7 @@ def make_v2(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_
output = collections.OrderedDict()
output["repo"] = v2_repo(repodict, repodir, archive)
if requestsdict and requestsdict["install"] or requestsdict["uninstall"]:
if requestsdict and (requestsdict["install"] or requestsdict["uninstall"]):
output["repo"]["requests"] = requestsdict
# establish sort order of the index
@ -792,7 +763,7 @@ def make_v2(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_
else:
json.dump(output, fp, default=_index_encoder_default, ensure_ascii=False)
entry["index"] = file_entry(index_file)
entry["index"] = common.file_entry(index_file)
entry["index"]["numPackages"] = len(output.get("packages", []))
indexes = sorted(Path().glob("tmp/{}*.json".format(repodir)), key=lambda x: x.name)
@ -819,7 +790,7 @@ def make_v2(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_
else:
json.dump(diff, fp, default=_index_encoder_default, ensure_ascii=False)
entry["diffs"][old["repo"]["timestamp"]] = file_entry(diff_file)
entry["diffs"][old["repo"]["timestamp"]] = common.file_entry(diff_file)
entry["diffs"][old["repo"]["timestamp"]]["numPackages"] = len(diff.get("packages", []))
json_name = "entry.json"
@ -872,10 +843,10 @@ def make_v1(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_
appslist = []
output['apps'] = appslist
for packageName, appdict in apps.items():
for packageName, app_dict in apps.items():
d = collections.OrderedDict()
appslist.append(d)
for k, v in sorted(appdict.items()):
for k, v in sorted(app_dict.items()):
if not v:
continue
if k in ('Builds', 'metadatapath',
@ -901,20 +872,20 @@ def make_v1(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_
d[k] = v
# establish sort order in lists, sets, and localized dicts
for app in output['apps']:
localized = app.get('localized')
for app_dict in output['apps']:
localized = app_dict.get('localized')
if localized:
lordered = collections.OrderedDict()
for lkey, lvalue in sorted(localized.items()):
lordered[lkey] = collections.OrderedDict()
for ikey, iname in sorted(lvalue.items()):
lordered[lkey][ikey] = iname
app['localized'] = lordered
antiFeatures = app.get('antiFeatures', [])
if apps[app["packageName"]].get("NoSourceSince"):
app_dict['localized'] = lordered
antiFeatures = app_dict.get('antiFeatures', [])
if apps[app_dict["packageName"]].get("NoSourceSince"):
antiFeatures.append("NoSourceSince")
if antiFeatures:
app['antiFeatures'] = sorted(set(antiFeatures))
app_dict['antiFeatures'] = sorted(set(antiFeatures))
output_packages = collections.OrderedDict()
output['packages'] = output_packages
@ -1050,7 +1021,7 @@ def make_v0(apps, apks, repodir, repodict, requestsdict, fdroid_signing_key_fing
lkey = key[:1].lower() + key[1:]
localized = app.get('localized')
if not value and localized:
for lang in ['en-US'] + [x for x in localized.keys()]:
for lang in [DEFAULT_LOCALE] + [x for x in localized.keys()]:
if not lang.startswith('en'):
continue
if lang in localized:
@ -1096,8 +1067,8 @@ def make_v0(apps, apks, repodir, repodict, requestsdict, fdroid_signing_key_fing
root.appendChild(element)
element.setAttribute('packageName', packageName)
for appid, appdict in apps.items():
app = metadata.App(appdict)
for appid, app_dict in apps.items():
app = metadata.App(app_dict)
if app.get('Disabled') is not None:
continue
@ -1294,7 +1265,7 @@ def make_v0(apps, apks, repodir, repodict, requestsdict, fdroid_signing_key_fing
namefield = common.config['current_version_name_source']
name = app.get(namefield)
if not name and namefield == 'Name':
name = app.get('localized', {}).get('en-US', {}).get('name')
name = app.get('localized', {}).get(DEFAULT_LOCALE, {}).get('name')
if not name:
name = app.id
sanitized_name = re.sub(b'''[ '"&%?+=/]''', b'', str(name).encode('utf-8'))

View file

@ -220,6 +220,19 @@ locale_pattern = re.compile(r"[a-z]{2,3}(-([A-Z][a-zA-Z]+|\d+|[a-z]+))*")
versioncode_check_pattern = re.compile(r"(\\d|\[(0-9|\\d)_?(a-fA-F)?])[+]")
ANTIFEATURES_KEYS = None
ANTIFEATURES_PATTERN = None
def load_antiFeatures_config():
"""Lazy loading, since it might read a lot of files."""
global ANTIFEATURES_KEYS, ANTIFEATURES_PATTERN
k = 'antiFeatures' # internal dict uses camelCase key name
if not ANTIFEATURES_KEYS or k not in common.config:
common.config[k] = common.load_localized_config(k, 'repo')
ANTIFEATURES_KEYS = sorted(common.config[k].keys())
ANTIFEATURES_PATTERN = ','.join(ANTIFEATURES_KEYS)
def check_regexes(app):
for f, checks in regex_checks.items():
@ -613,6 +626,26 @@ def check_app_field_types(app):
)
def check_antiFeatures(app):
"""Check the Anti-Features keys match those declared in the config."""
pattern = ANTIFEATURES_PATTERN
msg = _("'{value}' is not a valid {field} in {appid}. Regex pattern: {pattern}")
field = 'AntiFeatures' # App entries use capitalized CamelCase
for value in app.get(field, []):
if value not in ANTIFEATURES_KEYS:
yield msg.format(value=value, field=field, appid=app.id, pattern=pattern)
field = 'antifeatures' # Build entries use all lowercase
for build in app.get('Builds', []):
build_antiFeatures = build.get(field, [])
for value in build_antiFeatures:
if value not in ANTIFEATURES_KEYS:
yield msg.format(
value=value, field=field, appid=app.id, pattern=pattern
)
def check_for_unsupported_metadata_files(basedir=""):
"""Check whether any non-metadata files are in metadata/."""
basedir = Path(basedir)
@ -745,6 +778,7 @@ def main():
metadata.warnings_action = options.W
config = common.read_config(options)
load_antiFeatures_config()
# Get all apps...
allapps = metadata.read_metadata(options.appid)
@ -801,6 +835,7 @@ def main():
app_check_funcs = [
check_app_field_types,
check_antiFeatures,
check_regexes,
check_update_check_data_url,
check_update_check_data_int,

View file

@ -448,10 +448,6 @@ valuetypes = {
r'^[0-9]+ versions$',
["ArchivePolicy"]),
FieldValidator("Anti-Feature",
r'^(Ads|Tracking|NonFreeNet|NonFreeDep|NonFreeAdd|UpstreamNonFree|NonFreeAssets|KnownVuln|ApplicationDebuggable|NoSourceSince|NSFW)$',
["AntiFeatures"]),
FieldValidator("Auto Update Mode",
r"^(Version.*|None)$",
["AutoUpdateMode"]),
@ -676,7 +672,7 @@ def parse_metadata(metadatapath):
# pylint: disable-next=no-member
except git.exc.InvalidGitRepositoryError:
logging.debug(
_('Including metadata from {path}').format(metadata_in_repo)
_('Including metadata from {path}').format(path=metadata_in_repo)
)
app_in_repo = parse_metadata(metadata_in_repo)
for k, v in app_in_repo.items():

View file

@ -44,6 +44,22 @@ def proper_format(app):
return content == cur_content
def remove_blank_flags_from_builds(builds):
"""Remove unset entries from Builds so they are not written out."""
if not builds:
return list()
newbuilds = list()
for build in builds:
new = dict()
for k in metadata.build_flags:
v = build[k]
if v is None or v is False or v == [] or v == '':
continue
new[k] = v
newbuilds.append(new)
return newbuilds
def main():
global config, options
@ -82,16 +98,9 @@ def main():
print(path)
continue
newbuilds = []
for build in app.get('Builds', []):
new = metadata.Build()
for k in metadata.build_flags:
v = build[k]
if v is None or v is False or v == [] or v == '':
continue
new[k] = v
newbuilds.append(new)
app['Builds'] = newbuilds
builds = remove_blank_flags_from_builds(app.get('Builds'))
if builds:
app['Builds'] = builds
# rewrite to temporary file before overwriting existing
# file in case there's a bug in write_metadata

View file

@ -51,6 +51,7 @@ from . import _
from . import common
from . import index
from . import metadata
from .common import DEFAULT_LOCALE
from .exception import BuildException, FDroidException, VerificationException
from PIL import Image, PngImagePlugin
@ -1037,7 +1038,7 @@ def insert_localized_app_metadata(apps):
base = "iconv2"
if base not in apps[packageName] or not isinstance(apps[packageName][base], collections.OrderedDict):
apps[packageName][base] = collections.OrderedDict()
apps[packageName][base][locale] = index.file_entry(dst)
apps[packageName][base][locale] = common.file_entry(dst)
for d in dirs:
if d in SCREENSHOT_DIRS:
if locale == 'images':
@ -1090,7 +1091,7 @@ def insert_localized_app_metadata(apps):
base = "iconv2"
if base not in apps[packageName] or not isinstance(apps[packageName][base], collections.OrderedDict):
apps[packageName][base] = collections.OrderedDict()
apps[packageName][base][locale] = index.file_entry(index_file)
apps[packageName][base][locale] = common.file_entry(index_file)
elif screenshotdir in SCREENSHOT_DIRS:
# there can any number of these per locale
logging.debug(_('adding to {name}: {path}').format(name=screenshotdir, path=f))
@ -1105,7 +1106,7 @@ def insert_localized_app_metadata(apps):
apps[packageName]["screenshots"][newKey] = collections.OrderedDict()
if locale not in apps[packageName]["screenshots"][newKey]:
apps[packageName]["screenshots"][newKey][locale] = []
apps[packageName]["screenshots"][newKey][locale].append(index.file_entry(f))
apps[packageName]["screenshots"][newKey][locale].append(common.file_entry(f))
else:
logging.warning(_('Unsupported graphics file found: {path}').format(path=f))
@ -2034,7 +2035,7 @@ def insert_missing_app_names_from_apks(apps, apks):
The name from the APK is set as the default name for the app if
there is no other default set, e.g. app['Name'] or
app['localized']['en-US']['name']. The en-US locale is defined in
app['localized'][DEFAULT_LOCALE]['name']. The default is defined in
the F-Droid ecosystem as the locale of last resort, as in the one
that should always be present. en-US is used since it is the
locale of the source strings.
@ -2050,7 +2051,7 @@ def insert_missing_app_names_from_apks(apps, apks):
for appid, app in apps.items():
if app.get('Name') is not None:
continue
if app.get('localized', {}).get('en-US', {}).get('name') is not None:
if app.get('localized', {}).get(DEFAULT_LOCALE, {}).get('name') is not None:
continue
bestver = UNSET_VERSION_CODE
@ -2063,9 +2064,9 @@ def insert_missing_app_names_from_apks(apps, apks):
if bestver != UNSET_VERSION_CODE:
if 'localized' not in app:
app['localized'] = {}
if 'en-US' not in app['localized']:
app['localized']['en-US'] = {}
app['localized']['en-US']['name'] = bestapk.get('name')
if DEFAULT_LOCALE not in app['localized']:
app['localized'][DEFAULT_LOCALE] = {}
app['localized'][DEFAULT_LOCALE]['name'] = bestapk.get('name')
def get_apps_with_packages(apps, apks):
@ -2342,10 +2343,10 @@ def main():
add_apks_to_per_app_repos(repodirs[0], apks)
for appid, app in apps.items():
repodir = os.path.join(appid, 'fdroid', 'repo')
appdict = dict()
appdict[appid] = app
app_dict = dict()
app_dict[appid] = app
if os.path.isdir(repodir):
index.make(appdict, apks, repodir, False)
index.make(app_dict, apks, repodir, False)
else:
logging.info(_('Skipping index generation for {appid}').format(appid=appid))
return

View file

@ -0,0 +1,4 @@
Summary: This should be overridden by metadata/info.guardianproject.urzip.yml
Builds:
- versionCode: 50

View file

@ -2667,6 +2667,38 @@ class CommonTest(unittest.TestCase):
config['smartcardoptions'],
)
def test_load_localized_config(self):
"""It should load"""
antiFeatures = fdroidserver.common.load_localized_config('antiFeatures', 'repo')
self.assertEqual(
[
'Ads',
'DisabledAlgorithm',
'KnownVuln',
'NSFW',
'NoSourceSince',
'NonFreeAdd',
'NonFreeAssets',
'NonFreeDep',
'NonFreeNet',
'Tracking',
'UpstreamNonFree',
],
list(antiFeatures.keys()),
)
self.assertEqual(
['de', 'en-US', 'fa', 'ro', 'zh-rCN'],
list(antiFeatures['Ads']['description'].keys()),
)
self.assertEqual(
['en-US'],
list(antiFeatures['NoSourceSince']['description'].keys()),
)
# it should have copied the icon files into place
for v in antiFeatures.values():
p = Path(os.path.dirname(__file__) + '/repo' + v['icon']['en-US']['name'])
self.assertTrue(p.exists())
if __name__ == "__main__":
os.chdir(os.path.dirname(__file__))

View file

@ -0,0 +1,45 @@
Ads:
description: This app contains advertising
icon: ic_antifeature_ads.xml
name: Ads
DisabledAlgorithm:
description: This app has a weak security signature
icon: ic_antifeature_disabledalgorithm.xml
name: Signed Using An Unsafe Algorithm
KnownVuln:
description: This app contains a known security vulnerability
icon: ic_antifeature_knownvuln.xml
name: Known Vulnerability
NSFW:
description: This app contains content that should not be publicized or visible
everywhere
icon: ic_antifeature_nsfw.xml
name: NSFW
NoSourceSince:
description: The source code is no longer available, no updates possible.
icon: ic_antifeature_nosourcesince.xml
name: Newer Source Not Available
NonFreeAdd:
description: This app promotes non-free add-ons
icon: ic_antifeature_nonfreeadd.xml
name: Non-Free Addons
NonFreeAssets:
description: This app contains non-free assets
icon: ic_antifeature_nonfreeassets.xml
name: Non-Free Assets
NonFreeDep:
description: This app depends on other non-free apps
icon: ic_antifeature_nonfreedep.xml
name: Non-Free Dependencies
NonFreeNet:
description: This app promotes or depends entirely on a non-free network service
icon: ic_antifeature_nonfreenet.xml
name: Non-Free Network Services
Tracking:
description: This app tracks and reports your activity
icon: ic_antifeature_tracking.xml
name: Tracking
UpstreamNonFree:
description: The upstream source code is not entirely Free
icon: ic_antifeature_upstreamnonfree.xml
name: Upstream Non-Free

View file

@ -0,0 +1,44 @@
Ads:
description: Diese App enthält Werbung
icon: ic_antifeature_ads.xml
name: Werbung
DisabledAlgorithm:
description: Diese App hat eine schwache Sicherheitssignatur
icon: ic_antifeature_disabledalgorithm.xml
name: Mit einem unsicheren Algorithmus signiert
KnownVuln:
description: Diese App enthält eine bekannte Sicherheitslücke
icon: ic_antifeature_knownvuln.xml
name: Bekannte Sicherheitslücke
NSFW:
description: Diese App enthält Inhalte, die nicht überall veröffentlicht oder sichtbar
sein sollten
icon: ic_antifeature_nsfw.xml
name: NSFW
NoSourceSince:
icon: ic_antifeature_nosourcesince.xml
name: Der Quellcode ist nicht mehr erhältlich, keine Aktualisierungen möglich.
NonFreeAdd:
description: Diese App bewirbt nicht-quelloffene Erweiterungen
icon: ic_antifeature_nonfreeadd.xml
name: Nicht-quelloffene Erweiterungen
NonFreeAssets:
description: Diese App enthält nicht-quelloffene Bestandteile
icon: ic_antifeature_nonfreeassets.xml
name: Nicht-quelloffene Bestandteile
NonFreeDep:
description: Diese App ist abhängig von anderen nicht-quelloffenen Apps
icon: ic_antifeature_nonfreedep.xml
name: Nicht-quelloffene Abhängigkeiten
NonFreeNet:
description: Diese App bewirbt nicht-quelloffene Netzwerkdienste
icon: ic_antifeature_nonfreenet.xml
name: Nicht-quelloffene Netzwerkdienste
Tracking:
description: Diese App verfolgt und versendet Ihre Aktivitäten
icon: ic_antifeature_tracking.xml
name: Tracking
UpstreamNonFree:
description: Der Originalcode ist nicht völlig quelloffen
icon: ic_antifeature_upstreamnonfree.xml
name: Originalcode nicht-quelloffen

View file

@ -0,0 +1,43 @@
Ads:
description: این کاره دارای تبلیغات است
icon: ic_antifeature_ads.xml
name: تبلیغات
DisabledAlgorithm:
description: این کاره، امضای امنیتی ضعیفی دارد
icon: ic_antifeature_disabledalgorithm.xml
name: امضا شده با الگوریتمی ناامن
KnownVuln:
description: این کاره، آسیب‌پذیری امنیتی شناخته‌شده‌ای دارد
icon: ic_antifeature_knownvuln.xml
name: آسیب‌پذیری شناخته
NSFW:
description: این کاره محتوایی دارد که نباید عمومی شده یا همه‌حا نمایان باشد
icon: ic_antifeature_nsfw.xml
name: NSFW
NoSourceSince:
icon: ic_antifeature_nosourcesince.xml
name: کد مبدأ دیگر در دسترس نیست. به‌روز رسانی ناممکن است.
NonFreeAdd:
description: این کاره، افزونه‌های ناآزاد را تبلیغ می‌کند
icon: ic_antifeature_nonfreeadd.xml
name: افزونه‌های ناآزاد
NonFreeAssets:
description: این کاره دارای بخش‌های ناآزاد است
icon: ic_antifeature_nonfreeassets.xml
name: بخش‌های ناآزاد
NonFreeDep:
description: این کاره به دیگر کاره‌های ناآزاد وابسته است
icon: ic_antifeature_nonfreedep.xml
name: وابستگی‌های ناآزاد
NonFreeNet:
description: این کاره، خدمات شبکه‌های ناآزاد را ترویج می‌کند
icon: ic_antifeature_nonfreenet.xml
name: خدمات شبکه‌ای ناآزاد
Tracking:
description: این کاره، فعّالیتتان را ردیابی و گزارش می‌کند
icon: ic_antifeature_tracking.xml
name: ردیابی
UpstreamNonFree:
description: کد مبدأ بالادستی کاملاً آزاد نیست
icon: ic_antifeature_upstreamnonfree.xml
name: بالادست ناآزاد

View file

@ -0,0 +1,15 @@
<vector xmlns:tools="http://schemas.android.com/tools"
android:height="24dp"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal"
android:viewportWidth="48"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:ignore="VectorRaster">
<path
android:fillColor="#FF000000"
android:pathData="M32.56,11.19a3,3 0,0 0,-3.07 0.44h0a44.91,44.91 0,0 1,-13.55 8.07,2.67 2.67,0 0,0 -2.51,-2.08H8.76a2.84,2.84 0,0 0,-2.64 3.06v6A3.08,3.08 0,0 0,7.66 29.5c0.28,7 2.5,11.65 4.05,12.8a2.65,2.65 0,0 0,1.61 0.47,7.75 7.75,0 0,0 3.17,-0.89c1.55,-0.79 2.33,-1.67 2.32,-2.64A6.52,6.52 0,0 0,18 36.92,25 25,0 0,1 16.5,33a14.59,14.59 0,0 1,-0.44 -4.38,1.28 1.28,0 0,0 0,-0.37c2.13,0.41 7,1.91 13.44,7.49a3.13,3.13 0,0 0,2 0.73,2.63 2.63,0 0,0 1.1,-0.24c0.62,-0.28 1.66,-1.08 1.66,-3.19V14.34A3.13,3.13 0,0 0,32.56 11.19ZM8.71,20.69a0.78,0.78 0,0 1,0.13 -0.47h4.5a0.78,0.78 0,0 1,0.13 0.47v6a0.78,0.78 0,0 1,-0.13 0.47H8.85a0.78,0.78 0,0 1,-0.13 -0.47ZM16.13,39.1a5.6,5.6 0,0 1,-2.92 1.09c-0.63,-0.58 -2.62,-4.2 -2.94,-10.41h3.18A17.66,17.66 0,0 0,14 33.62,27.5 27.5,0 0,0 15.65,38C15.82,38.36 16,38.8 16.13,39.1ZM31.63,33a1.67,1.67 0,0 1,-0.14 0.83,0.47 0.47,0 0,1 -0.31,-0.09C24,27.52 18.51,26 16.07,25.6V22.37c2.44,-0.64 8.75,-3.41 15.09,-8.75h0a0.42,0.42 0,0 1,0.3 -0.07s0.16,0.24 0.16,0.8Z" />
<path
android:fillColor="#FF000000"
android:pathData="M40.86,13.17 L44,6.11a1.3,1.3 0,0 0,-2.37 -1l-4.72,10.7h5.27l-3.62,7A1.3,1.3 0,1 0,40.83 24l5.56,-10.82Z" />
</vector>

View file

@ -0,0 +1,21 @@
<vector xmlns:tools="http://schemas.android.com/tools"
android:height="24dp"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal"
android:viewportWidth="48"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:ignore="VectorRaster">
<path
android:fillColor="#FF000000"
android:pathData="M36.24,30.79l-2.18,-2.33A10,10 0,0 0,30.4 26a9.64,9.64 0,0 0,-1.28 -2.77A1.3,1.3 0,1 0,27 24.63a7.38,7.38 0,0 1,0.42 0.73,6.59 6.59,0 0,0 -4.63,1.71 6.51,6.51 0,0 0,-2 4.22,7.7 7.7,0 0,1 -1.48,-1.21l-2.18,-2.33A7.76,7.76 0,0 1,16 26.16,6.37 6.37,0 0,0 22.36,20a7.38,7.38 0,0 1,0.69 0.46,1.3 1.3,0 1,0 1.59,-2.05A9.65,9.65 0,0 0,22 16.92a10,10 0,0 0,-2.25 -3.83l-2.18,-2.33C14.26,7.24 9.18,6.62 6.23,9.37a6.74,6.74 0,0 0,-2 5.53,9.63 9.63,0 0,0 2.64,5.84L9,23.07a9.94,9.94 0,0 0,3.91 2.59,10.05 10.05,0 0,0 2.26,3.84l2.18,2.33A10,10 0,0 0,21 34.34a10,10 0,0 0,2.33 4.1l2.18,2.33a9.4,9.4 0,0 0,6.75 3.11,6.58 6.58,0 0,0 4.55,-1.72 6.74,6.74 0,0 0,2 -5.53A9.63,9.63 0,0 0,36.24 30.79ZM19.77,19.11a4.2,4.2 0,0 1,-1.22 3.46,4.18 4.18,0 0,1 -3.42,1s0,0 0,-0.06A4.2,4.2 0,0 1,16.35 20a4.2,4.2 0,0 1,3.41 -1S19.77,19.09 19.77,19.11ZM8.73,19a7,7 0,0 1,-2 -4.25A4.2,4.2 0,0 1,8 11.26a4,4 0,0 1,2.8 -1,6.84 6.84,0 0,1 4.85,2.3l2.18,2.33a7.7,7.7 0,0 1,1.11 1.57,6.4 6.4,0 0,0 -6.39,6.19 7.69,7.69 0,0 1,-1.63 -1.31ZM24.52,29a4.23,4.23 0,0 1,3.59 -1,3.94 3.94,0 0,1 -4.81,4.31A4.17,4.17 0,0 1,24.52 29ZM35.07,40.3C33.17,42 29.74,41.48 27.43,39l-2.18,-2.33a7.68,7.68 0,0 1,-1.2 -1.73h0.06A6.39,6.39 0,0 0,30.68 29a7.7,7.7 0,0 1,1.49 1.22l2.18,2.33a7,7 0,0 1,2 4.25A4.2,4.2 0,0 1,35.07 40.26Z" />
<path
android:fillColor="#FF000000"
android:pathData="M41.09,19.1a1.59,1.59 0,0 0,-1.87 -1.21l-4.57,1a1.54,1.54 0,0 0,-0.57 0.24h0a1.58,1.58 0,0 0,1.27 2.83l4.57,-1A1.55,1.55 0,0 0,41.09 19.1Z" />
<path
android:fillColor="#FF000000"
android:pathData="M33.93,16.3l0.06,-0.05 3.48,-3.1a1.58,1.58 0,0 0,-2 -2.41l-0.06,0.05 -3.48,3.1a1.58,1.58 0,0 0,2 2.41Z" />
<path
android:fillColor="#FF000000"
android:pathData="M27.14,14.49A1.54,1.54 0,0 0,29 13.34h0l1,-4.56a1.59,1.59 0,0 0,-1.15 -1.91,1.55 1.55,0 0,0 -1.49,0.4h0A1.54,1.54 0,0 0,27 8l-1,4.56A1.59,1.59 0,0 0,27.14 14.49Z" />
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:tools="http://schemas.android.com/tools"
android:height="24dp"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal"
android:viewportWidth="48"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:ignore="VectorRaster">
<path
android:fillColor="#FF000000"
android:pathData="M37.55,32.13a1.27,1.27 0,0 0,-0.43 0v-1.5h7.94a1.3,1.3 0,0 0,0 -2.59L37.12,28.04L37.12,26.71a1.24,1.24 0,0 0,0.18 0l0.25,0c3.44,-0.67 7.38,-5 8.61,-9.4a1.3,1.3 0,1 0,-2.5 -0.7c-1,3.7 -4.35,7.06 -6.55,7.53L37.11,20.58a1.3,1.3 0,0 0,-1.3 -1.3L33.54,19.28c0,-1.14 0,-3.88 0,-5a9.24,9.24 0,0 0,-18.47 0,1.3 1.3,0 1,0 2.59,0 6.64,6.64 0,0 1,13.29 0c0,1.08 0,3.81 0,5L13.14,19.28a1.3,1.3 0,0 0,-1.3 1.3v3.57c-2.2,-0.49 -5.51,-3.84 -6.53,-7.53a1.3,1.3 0,1 0,-2.5 0.7C4,21.75 8,26.06 11.43,26.72l0.25,0a1.24,1.24 0,0 0,0.17 0L11.85,28L3.65,28a1.3,1.3 0,1 0,0 2.59h8.2v1.5a1.26,1.26 0,0 0,-0.42 0c-3.44,0.67 -7.38,5 -8.61,9.4a1.3,1.3 0,1 0,2.5 0.7c1,-3.69 4.33,-7 6.53,-7.53v2.84a1.3,1.3 0,0 0,1.3 1.3L35.82,38.8a1.3,1.3 0,0 0,1.3 -1.3L37.12,34.69c2.2,0.48 5.52,3.83 6.55,7.53a1.3,1.3 0,1 0,2.5 -0.7C44.93,37.1 41,32.79 37.55,32.13ZM34.55,36.24L14.44,36.24L14.44,21.88L34.52,21.88Z" />
<path
android:fillColor="#FF000000"
android:pathData="M24.5,28.82m-2.44,0a2.44,2.44 0,1 1,4.88 0a2.44,2.44 0,1 1,-4.88 0" />
</vector>

View file

@ -0,0 +1,19 @@
<vector android:height="24dp"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal"
android:viewportWidth="48"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="#FF000000"
android:pathData="M32.83,26.35H29.65a2.68,2.68 0,0 0,-3 -2.27h-0.18c-2.4,0 -3.35,1.82 -3.43,4.5s1.54,4.54 3.43,4.5a2.73,2.73 0,0 0,3 -2.44h3.56c-0.59,3.34 -3,5.29 -6.53,5.3 -3.94,0 -6.8,-3.29 -6.8,-7.36 0,-4.34 2.85,-7.36 6.8,-7.36H27c2.46,0 5.81,1.77 5.81,5.13Z" />
<path
android:fillColor="#FF000000"
android:pathData="M16.28,23.15h0A1.87,1.87 0,0 1,18.14 25h0a1.87,1.87 0,0 1,-1.86 1.87h0A1.87,1.87 0,0 1,14.42 25h0A1.87,1.87 0,0 1,16.28 23.15Z" />
<path
android:fillColor="#FF000000"
android:pathData="M16.28,29.7h0a1.87,1.87 0,0 1,1.86 1.87h0a1.87,1.87 0,0 1,-1.86 1.87h0a1.87,1.87 0,0 1,-1.86 -1.87h0A1.87,1.87 0,0 1,16.28 29.7Z" />
<path
android:fillColor="#FF000000"
android:pathData="M11,41.81a1.5,1.5 0,0 1,-1.5 -1.5L9.5,16.24a1.5,1.5 0,0 1,1.5 -1.5h8.71l0,-0.82A4.54,4.54 0,0 1,17.9 10.6,3.62 3.62,0 0,1 19,8a7,7 0,0 1,5.08 -1.81c5.65,0 6.35,3.1 6.38,4.42A4.61,4.61 0,0 1,28.76 14v0.79h8.19a1.5,1.5 0,0 1,1.5 1.49l0,7.6a1.3,1.3 0,0 1,-1.29 1.3h0a1.3,1.3 0,0 1,-1.3 -1.29l0,-6.51h-8.4a1.3,1.3 0,0 1,-1.3 -1.3v-2.7a1.3,1.3 0,0 1,0.55 -1.06c0.49,-0.35 1.17,-1.1 1.15,-1.59 0,-1.25 -1.32,-1.89 -3.8,-1.9a4.62,4.62 0,0 0,-3.22 1,1.08 1.08,0 0,0 -0.36,0.73c0,0.88 1.09,1.55 1.1,1.55a1.3,1.3 0,0 1,0.65 1.05L22.4,16a1.3,1.3 0,0 1,-1.29 1.37h-9L12.11,39.21l23.74,0c0,-0.33 0,-0.74 0,-1.21 0,-1.43 0,-3.4 0,-5.17a1.3,1.3 0,0 1,1.29 -1.3h0a1.3,1.3 0,0 1,1.3 1.29c0,1.79 0,3.77 0,5.21 0,0.72 0,1.3 0,1.67 0,0.07 0,0.32 0,0.55 -0.11,1.45 -1.09,1.56 -1.39,1.56l-26,0ZM11,39.22h0ZM37,39.22h0Z" />
</vector>

View file

@ -0,0 +1,19 @@
<vector android:height="24dp"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal"
android:viewportWidth="48"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="#FF000000"
android:pathData="M17.08,18.06h0a1.87,1.87 0,0 1,1.86 1.87h0a1.87,1.87 0,0 1,-1.86 1.87h0a1.87,1.87 0,0 1,-1.86 -1.87h0A1.87,1.87 0,0 1,17.08 18.06Z" />
<path
android:fillColor="#FF000000"
android:pathData="M17.08,26.28h0a1.87,1.87 0,0 1,1.86 1.87h0A1.87,1.87 0,0 1,17.08 30h0a1.87,1.87 0,0 1,-1.86 -1.87h0A1.87,1.87 0,0 1,17.08 26.28Z" />
<path
android:fillColor="#FF000000"
android:pathData="M33.18,22H30a2.68,2.68 0,0 0,-3 -2.27h-0.18c-2.4,0 -3.35,1.82 -3.43,4.5s1.54,4.54 3.43,4.5a2.73,2.73 0,0 0,3 -2.44h3.56c-0.59,3.34 -3,5.29 -6.53,5.3 -3.94,0 -6.8,-3.29 -6.8,-7.36 0,-4.34 2.85,-7.36 6.8,-7.36h0.43c2.46,0 5.81,1.77 5.81,5.13Z" />
<path
android:fillColor="#FF000000"
android:pathData="M39.29,7.11h-30A3.85,3.85 0,0 0,6.44 8.37h0v0a3.59,3.59 0,0 0,-0.9 2.37L5.54,37.63a3.75,3.75 0,0 0,3.8 3.68h30a3.75,3.75 0,0 0,3.8 -3.68L43.14,10.79A3.75,3.75 0,0 0,39.29 7.11ZM39.48,9.71a1.86,1.86 0,1 1,-1.85 1.86A1.85,1.85 0,0 1,39.48 9.71ZM39.48,17.93a1.86,1.86 0,1 1,-1.85 1.86A1.85,1.85 0,0 1,39.48 17.94ZM9.06,38.34h0a1.87,1.87 0,0 1,0 -3.75h0a1.87,1.87 0,0 1,0 3.75ZM9.06,30.05h0a1.87,1.87 0,0 1,0 -3.75h0a1.87,1.87 0,0 1,0 3.75ZM9.06,21.76h0a1.87,1.87 0,0 1,0 -3.75h0a1.87,1.87 0,0 1,0 3.75ZM9.06,13.47h0a1.87,1.87 0,0 1,0 -3.75h0a1.87,1.87 0,0 1,0 3.75ZM36,38.71L12.23,38.71L12.23,9.7L36,9.7ZM39.51,38.33a1.87,1.87 0,1 1,1.86 -1.87A1.87,1.87 0,0 1,39.48 38.34ZM39.51,30.04a1.87,1.87 0,1 1,1.86 -1.87A1.87,1.87 0,0 1,39.48 30Z" />
</vector>

View file

@ -0,0 +1,22 @@
<vector android:height="24dp"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal"
android:viewportWidth="48"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="#FF000000"
android:pathData="M9.38,17.97a1.49,1.46 0,1 0,2.98 0a1.49,1.46 0,1 0,-2.98 0z" />
<path
android:fillColor="#FF000000"
android:pathData="M34.73,9.72a1.3,1.3 0,0 0,-1.3 1.3V33.07l-7.19,-5.52a12,12 0,1 0,-1.67 2l8.29,6.37a1.27,1.27 0,0 0,0.56 0.23v6.4a1.3,1.3 0,1 0,2.59 0V11A1.3,1.3 0,0 0,34.73 9.72ZM16.33,30.27a9.33,9.33 0,1 1,9.33 -9.33A9.34,9.34 0,0 1,16.33 30.27Z" />
<path
android:fillColor="#FF000000"
android:pathData="M41.67,9.72a1.3,1.3 0,0 0,-1.3 1.3V42.54a1.3,1.3 0,1 0,2.59 0V11A1.3,1.3 0,0 0,41.67 9.72Z" />
<path
android:fillColor="#FF000000"
android:pathData="M9.38,23.81a1.49,1.46 0,1 0,2.98 0a1.49,1.46 0,1 0,-2.98 0z" />
<path
android:fillColor="#FF000000"
android:pathData="M20.08,22.43A1.74,1.74 0,0 1,18.13 24h0a1.93,1.93 0,0 1,-1.38 -0.59,3.12 3.12,0 0,1 -0.78,-2.32c0.06,-1.94 0.8,-2.92 2.2,-2.92h0.18a1.68,1.68 0,0 1,1.83 1.44v0.07h2.28v-0.09c0,-2.34 -2.36,-3.48 -4,-3.5h-0.28c-2.72,0 -4.62,2.05 -4.62,5A5.23,5.23 0,0 0,15 24.77a4.41,4.41 0,0 0,3.14 1.29h0a4.19,4.19 0,0 0,4.43 -3.6l0,-0.1H20.09Z" />
</vector>

View file

@ -0,0 +1,37 @@
<vector android:height="24dp"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal"
android:viewportWidth="48"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="#FF000000"
android:pathData="M13.65,31.74L13,31.74a10.63,10.63 0,0 1,0 -21.25C15.18,7.25 19.22,4.1 23.66,3.9h0c6.23,-0.23 11.08,3.49 13.09,10a8.91,8.91 0,0 1,0 17.82L13.65,31.72ZM13.21,13.06A8,8 0,0 0,13 29.14L36.69,29.14a6.32,6.32 0,0 0,-0.23 -12.64l-0.54,0 -1.11,0.11 -0.27,-1.08c-0.72,-2.82 -3.19,-9.35 -10.76,-9.07h0c-3.23,0.14 -7,2.61 -9.06,6l-0.42,0.67 -0.78,-0.06Z" />
<path
android:fillColor="#FF000000"
android:pathData="M15.33,14.71h0a1.87,1.87 0,0 1,1.86 1.87h0a1.87,1.87 0,0 1,-1.86 1.87h0a1.87,1.87 0,0 1,-1.86 -1.87h0A1.87,1.87 0,0 1,15.33 14.71Z" />
<path
android:fillColor="#FF000000"
android:pathData="M15.33,20.77h0a1.87,1.87 0,0 1,1.86 1.87h0a1.87,1.87 0,0 1,-1.86 1.87h0a1.87,1.87 0,0 1,-1.86 -1.87h0A1.87,1.87 0,0 1,15.33 20.77Z" />
<path
android:fillColor="#FF000000"
android:pathData="M36.27,39.92a1.3,1.3 0,0 1,-1.1 -2l2.28,-3.68a1.3,1.3 0,1 1,2.2 1.37l-2.28,3.68A1.3,1.3 0,0 1,36.27 39.92Z" />
<path
android:fillColor="#FF000000"
android:pathData="M27.92,43.41a1.3,1.3 0,0 1,-1.1 -2l2.28,-3.68a1.3,1.3 0,1 1,2.2 1.37L29,42.79A1.3,1.3 0,0 1,27.92 43.41Z" />
<path
android:fillColor="#FF000000"
android:pathData="M22.17,39.92a1.3,1.3 0,0 1,-1.1 -2l2.28,-3.68a1.3,1.3 0,1 1,2.2 1.37l-2.28,3.68A1.3,1.3 0,0 1,22.17 39.92Z" />
<path
android:fillColor="#FF000000"
android:pathData="M13.83,43.41a1.3,1.3 0,0 1,-1.1 -2L15,37.75a1.3,1.3 0,1 1,2.2 1.37l-2.28,3.68A1.3,1.3 0,0 1,13.83 43.41Z" />
<path
android:fillColor="#FF000000"
android:pathData="M8.64,39.84a1.3,1.3 0,0 1,-1.1 -2l2.28,-3.68A1.3,1.3 0,0 1,12 35.55L9.74,39.23A1.3,1.3 0,0 1,8.64 39.84Z" />
<path
android:fillColor="#FF000000"
android:pathData="M30.28,17.37H27.56A2.3,2.3 0,0 0,25 15.43h-0.15c-2.06,0 -2.87,1.56 -2.94,3.85s1.32,3.89 2.94,3.85A2.34,2.34 0,0 0,27.48 21h3.05a5.28,5.28 0,0 1,-5.59 4.54c-3.38,0 -5.82,-2.82 -5.82,-6.3 0,-3.72 2.44,-6.3 5.82,-6.3h0.37C27.42,13 30.28,14.5 30.28,17.37Z" />
<path
android:fillColor="#FF000000"
android:pathData="M24.93,25.63a5.61,5.61 0,0 1,-4 -1.64,6.56 6.56,0 0,1 -1.86,-4.7 6.65,6.65 0,0 1,1.65 -4.57,5.67 5.67,0 0,1 4.21,-1.77h0.37c2,0 5,1.47 5,4.44v0H27.52v0a2.23,2.23 0,0 0,-2.42 -1.91h-0.23c-1.85,0 -2.82,1.28 -2.9,3.81a4.07,4.07 0,0 0,1 3,2.55 2.55,0 0,0 1.87,0.78A2.3,2.3 0,0 0,27.44 21v0h3.14v0.05a5.32,5.32 0,0 1,-5.64 4.57ZM25.14,13h-0.2c-3.4,0 -5.78,2.57 -5.78,6.26A6.48,6.48 0,0 0,21 23.93a5.53,5.53 0,0 0,3.93 1.62h0a5.24,5.24 0,0 0,5.54 -4.45h-3a2.39,2.39 0,0 1,-2.64 2.09h-0.05a2.63,2.63 0,0 1,-1.88 -0.8,4.15 4.15,0 0,1 -1.05,-3.1c0.08,-2.59 1.08,-3.9 3,-3.9h0.22a2.34,2.34 0,0 1,2.5 1.94h2.65c0,-2.88 -3,-4.27 -4.94,-4.31h-0.16Z" />
</vector>

View file

@ -0,0 +1,16 @@
<vector android:height="24dp"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal"
android:viewportWidth="48"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="#FF000000"
android:pathData="M42,31.65L40.6,31.65L40.6,21.46a1.24,1.24 0,0 0,0 -0.26,16.08 16.08,0 0,0 -32.16,0.05 1.31,1.31 0,0 0,0 0.21v10.2L6.9,31.66a1.3,1.3 0,0 0,-1.3 1.3v7.36a1.3,1.3 0,0 0,1.3 1.3L42,41.62a1.3,1.3 0,0 0,1.3 -1.3L43.3,32.95A1.3,1.3 0,0 0,42 31.65ZM11,21.65a1.3,1.3 0,0 0,0 -0.18,13.49 13.49,0 0,1 27,0 1.24,1.24 0,0 0,0 0.24v10L11,31.71ZM40.74,39L8.2,39L8.2,34.25L40.74,34.25Z" />
<path
android:fillColor="#FF000000"
android:pathData="M28.27,16.23a1.3,1.3 0,0 0,-1.82 1.85l3.9,3.82 -3.9,3.82a1.3,1.3 0,0 0,1.82 1.85l4.84,-4.75a1.3,1.3 0,0 0,0 -1.85Z" />
<path
android:fillColor="#FF000000"
android:pathData="M22.52,16.25a1.3,1.3 0,0 0,-1.83 0L15.84,21a1.3,1.3 0,0 0,0 1.85l4.84,4.75a1.3,1.3 0,0 0,1.82 -1.85L18.6,21.9l3.9,-3.82A1.3,1.3 0,0 0,22.52 16.25Z" />
</vector>

View file

@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="48"
android:viewportWidth="48" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M21.99,12.34C22,12.23 22,12.11 22,12c0,-5.52 -4.48,-10 -10,-10S2,6.48 2,12c0,5.17 3.93,9.43 8.96,9.95c-0.93,-0.73 -1.72,-1.64 -2.32,-2.68C5.9,18 4,15.22 4,12c0,-1.85 0.63,-3.55 1.69,-4.9l5.66,5.66c0.56,-0.4 1.17,-0.73 1.82,-1L7.1,5.69C8.45,4.63 10.15,4 12,4c4.24,0 7.7,3.29 7.98,7.45C20.69,11.67 21.37,11.97 21.99,12.34zM17,13c-3.18,0 -5.9,1.87 -7,4.5c1.1,2.63 3.82,4.5 7,4.5s5.9,-1.87 7,-4.5C22.9,14.87 20.18,13 17,13zM17,20c-1.38,0 -2.5,-1.12 -2.5,-2.5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5C19.5,18.88 18.38,20 17,20zM18.5,17.5c0,0.83 -0.67,1.5 -1.5,1.5s-1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5S18.5,16.67 18.5,17.5z"/>
</vector>

View file

@ -0,0 +1,22 @@
<vector android:height="24dp"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal"
android:viewportWidth="48"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="#FF000000"
android:pathData="M6.32,34.68a2.83,2.83 0,0 1,-2.54 -1.4,3.12 3.12,0 0,1 0.12,-3.16L9.68,20.3l0.25,-0.45c0.72,-1.31 1.25,-1.49 2.82,-1.5h0l20.2,-0.06h0c1.48,0 2,0.2 2.73,1.43l0.29,0.49L42.12,30a3.07,3.07 0,0 1,0.17 3.14,2.88 2.88,0 0,1 -2.58,1.44l-33.38,0.06ZM12.32,20.96 L12.24,21.11 11.95,21.62L6.13,31.44A0.62,0.62 0,0 0,6 32c0,0.08 0.24,0.09 0.29,0.09h0L39.71,32c0.08,0 0.26,0 0.31,-0.11a0.57,0.57 0,0 0,-0.1 -0.52l-6.11,-9.82c-0.14,-0.22 -0.25,-0.41 -0.34,-0.57l-0.07,-0.12L33,20.86l-20.2,0.06Z" />
<path
android:fillColor="#FF000000"
android:pathData="M22.94,18.19a8,8 0,0 0,-8.2 7.89A8,8 0,0 0,22.94 34a8,8 0,0 0,8.2 -7.89A8,8 0,0 0,22.94 18.19ZM22.94,31.12a5,5 0,1 1,5.26 -5A5.16,5.16 0,0 1,22.94 31.12Z" />
<path
android:fillColor="#FF000000"
android:pathData="M22.94,24h0a2.1,2.1 0,0 1,2.14 2.05h0a2.1,2.1 0,0 1,-2.14 2.05h0a2.1,2.1 0,0 1,-2.14 -2.05h0A2.1,2.1 0,0 1,22.94 24Z" />
<path
android:fillColor="#FF000000"
android:pathData="M42.91,29.16a6.72,6.72 0,0 1,-3.12 -1c-0.71,-0.39 -4.24,-2.45 -4.23,-4.5s3.48,-4 4.18,-4.42c2.92,-1.6 3.94,-0.94 4.28,-0.73 1.24,0.79 1.48,3.31 1.47,5.28 0,2.77 -0.51,4.49 -1.49,5.1A1.89,1.89 0,0 1,42.91 29.16ZM42.47,26.8ZM38.21,23.62a10.84,10.84 0,0 0,4.39 2.91,13.3 13.3,0 0,0 0,-5.75A10.64,10.64 0,0 0,38.21 23.62ZM38.13,23.77ZM38.13,23.47ZM42.87,20.75h0ZM42.47,20.52Z" />
<path
android:fillColor="#FF000000"
android:pathData="M42.82,42.72H3.15A1.3,1.3 0,0 1,2 40.79l3.49,-6.2a2.72,2.72 0,0 1,-1.74 -1.3,3.12 3.12,0 0,1 0.12,-3.16L9.68,20.3l0.25,-0.45c0.72,-1.31 1.25,-1.49 2.82,-1.5h1.91L21.51,6.17a1.3,1.3 0,0 1,1.12 -0.66,1.28 1.28,0 0,1 1.13,0.64L30.85,18.3h2.1c1.52,0 2.06,0.18 2.77,1.43 0.08,0.14 0.17,0.3 0.29,0.49L42.12,30a3.07,3.07 0,0 1,0.17 3.14,2.8 2.8,0 0,1 -2,1.38l3.62,6.2a1.3,1.3 0,0 1,-1.12 1.95ZM5.37,40.13H40.56L37,34A1.3,1.3 0,0 1,38.1 32h1.61c0.08,0 0.26,0 0.31,-0.11a0.57,0.57 0,0 0,-0.1 -0.52l-6.11,-9.82c-0.14,-0.23 -0.25,-0.41 -0.34,-0.57l-0.07,-0.12h-3.3A1.31,1.31 0,0 1,29 20.26L22.66,9.42 16.54,20.28a1.3,1.3 0,0 1,-1.13 0.66H12.29l-0.08,0.15 -0.29,0.51L6.13,31.44A0.62,0.62 0,0 0,6 32c0,0.08 0.24,0.09 0.29,0.09H7.68A1.3,1.3 0,0 1,8.81 34Z" />
</vector>

View file

@ -0,0 +1,19 @@
<vector android:height="24dp"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal"
android:viewportWidth="48"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="#FF000000"
android:pathData="M15.72,22.64a1.86,1.87 0,1 0,3.72 0a1.86,1.87 0,1 0,-3.72 0z" />
<path
android:fillColor="#FF000000"
android:pathData="M15.72,29.24a1.86,1.87 0,1 0,3.72 0a1.86,1.87 0,1 0,-3.72 0z" />
<path
android:fillColor="#FF000000"
android:pathData="M20.28,26.12c0,4.06 2.85,7.37 6.8,7.36 3.49,0 5.94,-2 6.53,-5.3H30a2.73,2.73 0,0 1,-3 2.44c-1.89,0 -3.51,-1.7 -3.43,-4.5s1,-4.5 3.43,-4.5h0.18a2.68,2.68 0,0 1,3 2.27h3.18c0,-3.36 -3.35,-5.08 -5.81,-5.13h-0.43C23.13,18.76 20.28,21.78 20.28,26.12Z" />
<path
android:fillColor="#FF000000"
android:pathData="M24.375,8.9277A1.3,1.3 0,0 0,23.7793 9.0801L5.4492,18.8008A1.3,1.3 0,0 0,6.6699 21.0898L10.1992,19.2109L10.1992,36.6699A3.07,3.07 0,0 0,13.3594 39.6699L35.6406,39.6699A3.07,3.07 0,0 0,38.8008 36.6699L38.8008,19.4609L42.3301,21.3594A1.3,1.3 0,0 0,43.5605 19.0801L43.5605,19.0703L25,9.0801A1.3,1.3 0,0 0,24.375 8.9277zM24.3691,11.6992L36.2207,18.0703L36.2109,36.6699C36.2109,36.8399 35.9706,37.0293 35.6406,37.0293L13.3594,37.0293C13.0394,37.0293 12.7891,36.8399 12.7891,36.6699L12.7891,17.9492L12.7891,17.8496L24.3691,11.6992z" />
</vector>

View file

@ -0,0 +1,44 @@
Ads:
description: Aplicația conține reclamă
icon: ic_antifeature_ads.xml
name: Reclame
DisabledAlgorithm:
description: Aplicația are o semnătură slab securizată
icon: ic_antifeature_disabledalgorithm.xml
name: Algoritm nesigur semnătură
KnownVuln:
description: Aplicația conține o vulnerabilitate de securitate cunoscută
icon: ic_antifeature_knownvuln.xml
name: Vulnerabilitate cunoscută
NSFW:
description: Această aplicație conține conținut care nu ar trebui să fie făcut public
sau vizibil peste tot
icon: ic_antifeature_nsfw.xml
name: NSFW
NoSourceSince:
icon: ic_antifeature_nosourcesince.xml
name: Codul sursă nu mai este disponibil, nu mai există posibilitatea de a actualiza.
NonFreeAdd:
description: Aplicația promovează anexe ce nu sunt software liber
icon: ic_antifeature_nonfreeadd.xml
name: Anexe ne-libere
NonFreeAssets:
description: Aceasta aplicație conține resurse ce nu sunt la disponibile la liber
icon: ic_antifeature_nonfreeassets.xml
name: Resurse ne-libere
NonFreeDep:
description: Aplicația depinde de alte aplicații ce nu sunt software liber
icon: ic_antifeature_nonfreedep.xml
name: Dependențe ne-libere
NonFreeNet:
description: Aplicația promovează servicii de rețea ce nu sunt accesibile la liber
icon: ic_antifeature_nonfreenet.xml
name: Servicii de rețea ne-libere
Tracking:
description: Aplicația îți înregistrează și raportează activitatea undeva
icon: ic_antifeature_tracking.xml
name: Urmărire
UpstreamNonFree:
description: Codul sursa originar nu este în totalitatea lui software liber
icon: ic_antifeature_upstreamnonfree.xml
name: Surse ne-libere

View file

@ -0,0 +1,43 @@
Ads:
description: 此应用包含广告
icon: ic_antifeature_ads.xml
name: 广告
DisabledAlgorithm:
description: 此应用的安全签名较弱
icon: ic_antifeature_disabledalgorithm.xml
name: 使用不安全算法签名
KnownVuln:
description: 此应用包含已知的安全漏洞
icon: ic_antifeature_knownvuln.xml
name: 含有已知漏洞
NSFW:
description: 此应用包含不应宣扬或随处可见的内容
icon: ic_antifeature_nsfw.xml
name: NSFW
NoSourceSince:
icon: ic_antifeature_nosourcesince.xml
name: 源代码不再可用,无法更新。
NonFreeAdd:
description: 此应用推广非自由的附加组件
icon: ic_antifeature_nonfreeadd.xml
name: 非自由附加组件
NonFreeAssets:
description: 此应用包含非自由资源
icon: ic_antifeature_nonfreeassets.xml
name: 非自由资产
NonFreeDep:
description: 此应用依赖于其它非自由应用
icon: ic_antifeature_nonfreedep.xml
name: 非自由依赖项
NonFreeNet:
description: 此应用推广非自由的网络服务
icon: ic_antifeature_nonfreenet.xml
name: 非自由网络服务
Tracking:
description: 此应用会记录并报告你的活动
icon: ic_antifeature_tracking.xml
name: 跟踪用户
UpstreamNonFree:
description: 上游源代码不是完全自由的
icon: ic_antifeature_upstreamnonfree.xml
name: 上游代码非自由

View file

@ -132,23 +132,9 @@ class LintTest(unittest.TestCase):
app.Description = 'These are some back'
fields = {
'AntiFeatures': {
'good': [
[
'KnownVuln',
],
['NonFreeNet', 'KnownVuln'],
],
'bad': [
'KnownVuln',
'NonFreeNet,KnownVuln',
],
},
'Categories': {
'good': [
[
'Sports & Health',
],
['Sports & Health'],
['Multimedia', 'Graphics'],
],
'bad': [
@ -328,6 +314,53 @@ class LintTest(unittest.TestCase):
self.assertFalse(anywarns)
class LintAntiFeaturesTest(unittest.TestCase):
def setUp(self):
self.basedir = localmodule / 'tests'
os.chdir(self.basedir)
fdroidserver.common.config = dict()
fdroidserver.lint.load_antiFeatures_config()
def test_check_antiFeatures_empty(self):
app = fdroidserver.metadata.App()
self.assertEqual([], list(fdroidserver.lint.check_antiFeatures(app)))
def test_check_antiFeatures_empty_AntiFeatures(self):
app = fdroidserver.metadata.App()
app['AntiFeatures'] = []
self.assertEqual([], list(fdroidserver.lint.check_antiFeatures(app)))
def test_check_antiFeatures(self):
app = fdroidserver.metadata.App()
app['AntiFeatures'] = ['Ads', 'UpstreamNonFree']
self.assertEqual([], list(fdroidserver.lint.check_antiFeatures(app)))
def test_check_antiFeatures_fails_one(self):
app = fdroidserver.metadata.App()
app['AntiFeatures'] = ['Ad']
self.assertEqual(1, len(list(fdroidserver.lint.check_antiFeatures(app))))
def test_check_antiFeatures_fails_many(self):
app = fdroidserver.metadata.App()
app['AntiFeatures'] = ['Adss', 'Tracker', 'NoSourceSince', 'FAKE', 'NonFree']
self.assertEqual(4, len(list(fdroidserver.lint.check_antiFeatures(app))))
def test_check_antiFeatures_build_empty(self):
app = fdroidserver.metadata.App()
app['Builds'] = [{'antifeatures': []}]
self.assertEqual([], list(fdroidserver.lint.check_antiFeatures(app)))
def test_check_antiFeatures_build(self):
app = fdroidserver.metadata.App()
app['Builds'] = [{'antifeatures': ['Tracking']}]
self.assertEqual(0, len(list(fdroidserver.lint.check_antiFeatures(app))))
def test_check_antiFeatures_build_fail(self):
app = fdroidserver.metadata.App()
app['Builds'] = [{'antifeatures': ['Ads', 'Tracker']}]
self.assertEqual(1, len(list(fdroidserver.lint.check_antiFeatures(app))))
if __name__ == "__main__":
parser = optparse.OptionParser()
parser.add_option(

View file

@ -14,8 +14,9 @@ import tempfile
import textwrap
from collections import OrderedDict
from pathlib import Path
from unittest import mock
from testcommon import TmpCwd
from testcommon import TmpCwd, mkdtemp
localmodule = Path(__file__).resolve().parent.parent
print('localmodule: ' + str(localmodule))
@ -40,10 +41,13 @@ class MetadataTest(unittest.TestCase):
logging.basicConfig(level=logging.DEBUG)
self.basedir = localmodule / 'tests'
os.chdir(self.basedir)
self._td = mkdtemp()
self.testdir = self._td.name
fdroidserver.metadata.warnings_action = 'error'
def tearDown(self):
# auto-generated dirs by functions, not tests, so they are not always cleaned up
self._td.cleanup()
try:
os.rmdir("srclibs")
except OSError:
@ -178,26 +182,6 @@ class MetadataTest(unittest.TestCase):
'fake.app.id',
)
def test_check_metadata_AntiFeatures(self):
fdroidserver.metadata.warnings_action = 'error'
app = fdroidserver.metadata.App()
self.assertIsNone(metadata.check_metadata(app))
app['AntiFeatures'] = []
self.assertIsNone(metadata.check_metadata(app))
app['AntiFeatures'] = ['Ads', 'UpstreamNonFree']
self.assertIsNone(metadata.check_metadata(app))
app['AntiFeatures'] = ['Ad']
with self.assertRaises(fdroidserver.exception.MetaDataException):
metadata.check_metadata(app)
app['AntiFeatures'] = ['Adss']
with self.assertRaises(fdroidserver.exception.MetaDataException):
metadata.check_metadata(app)
def test_valid_funding_yml_regex(self):
"""Check the regex can find all the cases"""
with (self.basedir / 'funding-usernames.yaml').open() as fp:
@ -218,8 +202,10 @@ class MetadataTest(unittest.TestCase):
m, 'this is a valid %s username: {%s}' % (k, entry)
)
def test_read_metadata(self):
@mock.patch('git.Repo')
def test_read_metadata(self, git_repo):
"""Read specified metadata files included in tests/, compare to stored output"""
self.maxDiff = None
config = dict()
@ -248,7 +234,27 @@ class MetadataTest(unittest.TestCase):
# yaml.register_class(metadata.Build)
# yaml.dump(frommeta, fp)
def test_rewrite_yaml_fakeotaupdate(self):
@mock.patch('git.Repo')
def test_metadata_overrides_dot_fdroid_yml(self, git_Repo):
"""Fields in metadata files should override anything in .fdroid.yml."""
app = metadata.parse_metadata('metadata/info.guardianproject.urzip.yml')
self.assertEqual(app['Summary'], '一个实用工具,获取已安装在您的设备上的应用的有关信息')
def test_dot_fdroid_yml_works_without_git(self):
"""Parsing should work if .fdroid.yml is present and it is not a git repo."""
os.chdir(self.testdir)
yml = Path('metadata/test.yml')
yml.parent.mkdir()
with yml.open('w') as fp:
fp.write('Repo: https://example.com/not/git/or/anything')
fdroid_yml = Path('build/test/.fdroid.yml')
fdroid_yml.parent.mkdir(parents=True)
with fdroid_yml.open('w') as fp:
fp.write('OpenCollective: test')
metadata.parse_metadata(yml) # should not throw an exception
@mock.patch('git.Repo')
def test_rewrite_yaml_fakeotaupdate(self, git_Repo):
with tempfile.TemporaryDirectory() as testdir:
testdir = Path(testdir)
fdroidserver.common.config = {'accepted_formats': ['yml']}
@ -270,7 +276,8 @@ class MetadataTest(unittest.TestCase):
(Path('metadata-rewrite-yml') / file_name).read_text(encoding='utf-8'),
)
def test_rewrite_yaml_fdroidclient(self):
@mock.patch('git.Repo')
def test_rewrite_yaml_fdroidclient(self, git_Repo):
with tempfile.TemporaryDirectory() as testdir:
testdir = Path(testdir)
fdroidserver.common.config = {'accepted_formats': ['yml']}
@ -291,7 +298,8 @@ class MetadataTest(unittest.TestCase):
(Path('metadata-rewrite-yml') / file_name).read_text(encoding='utf-8'),
)
def test_rewrite_yaml_special_build_params(self):
@mock.patch('git.Repo')
def test_rewrite_yaml_special_build_params(self, git_Repo):
with tempfile.TemporaryDirectory() as testdir:
testdir = Path(testdir)
@ -450,7 +458,6 @@ class MetadataTest(unittest.TestCase):
with self.assertRaises(TypeError):
metadata.parse_yaml_metadata(mf)
mf.name = 'mock_filename.yaml'
self.assertEqual(fdroidserver.metadata.parse_yaml_metadata(mf), dict())
def test_parse_yaml_metadata_unknown_app_field(self):
@ -481,7 +488,9 @@ class MetadataTest(unittest.TestCase):
with self.assertRaises(MetaDataException):
fdroidserver.metadata.parse_yaml_metadata(mf)
def test_parse_yaml_metadata_continue_on_warning(self):
@mock.patch('logging.warning')
@mock.patch('logging.error')
def test_parse_yaml_metadata_continue_on_warning(self, _error, _warning):
"""When errors are disabled, parsing should provide something that can work.
When errors are disabled, then it should try to give data that
@ -495,6 +504,8 @@ class MetadataTest(unittest.TestCase):
fdroidserver.metadata.warnings_action = None
mf = _get_mock_mf('[AntiFeatures: Tracking]')
self.assertEqual(fdroidserver.metadata.parse_yaml_metadata(mf), dict())
_warning.assert_called_once()
_error.assert_called_once()
def test_parse_yaml_srclib_corrupt_file(self):
with tempfile.TemporaryDirectory() as testdir:

View file

@ -3,8 +3,8 @@
"version": 20002,
"index": {
"name": "/index-v2.json",
"sha256": "07fa4500736ae77fcc6434e4d70ab315b8e018aef52c2afca9f2834ddc73747d",
"size": 32946,
"sha256": "a3c7e88a522a7228937e5c3d760fc239e3578e292035d88478d32fec9ff5eb54",
"size": 52314,
"numPackages": 10
},
"diffs": {}

View file

@ -27,6 +27,477 @@
}
],
"timestamp": 1676634233000,
"antiFeatures": {
"Ads": {
"description": {
"de": "Diese App enthält Werbung",
"en-US": "This app contains advertising",
"fa": "این کاره دارای تبلیغات است",
"ro": "Aplicația conține reclamă",
"zh-rCN": "此应用包含广告"
},
"icon": {
"de": {
"name": "/icons/ic_antifeature_ads.xml",
"sha256": "b333528573134c5de73484862a1b567a0bdfd6878d183f8500287abadc0ba60e",
"size": 1564
},
"en-US": {
"name": "/icons/ic_antifeature_ads.xml",
"sha256": "b333528573134c5de73484862a1b567a0bdfd6878d183f8500287abadc0ba60e",
"size": 1564
},
"fa": {
"name": "/icons/ic_antifeature_ads.xml",
"sha256": "b333528573134c5de73484862a1b567a0bdfd6878d183f8500287abadc0ba60e",
"size": 1564
},
"ro": {
"name": "/icons/ic_antifeature_ads.xml",
"sha256": "b333528573134c5de73484862a1b567a0bdfd6878d183f8500287abadc0ba60e",
"size": 1564
},
"zh-rCN": {
"name": "/icons/ic_antifeature_ads.xml",
"sha256": "b333528573134c5de73484862a1b567a0bdfd6878d183f8500287abadc0ba60e",
"size": 1564
}
},
"name": {
"de": "Werbung",
"en-US": "Ads",
"fa": "تبلیغات",
"ro": "Reclame",
"zh-rCN": "广告"
}
},
"DisabledAlgorithm": {
"description": {
"de": "Diese App hat eine schwache Sicherheitssignatur",
"en-US": "This app has a weak security signature",
"fa": "این کاره، امضای امنیتی ضعیفی دارد",
"ro": "Aplicația are o semnătură slab securizată",
"zh-rCN": "此应用的安全签名较弱"
},
"icon": {
"de": {
"name": "/icons/ic_antifeature_disabledalgorithm.xml",
"sha256": "94dea590c7c0aa37d351ab62a69fc7eefbc2cdbb84b79df3934c2e9332e1dcfb",
"size": 2313
},
"en-US": {
"name": "/icons/ic_antifeature_disabledalgorithm.xml",
"sha256": "94dea590c7c0aa37d351ab62a69fc7eefbc2cdbb84b79df3934c2e9332e1dcfb",
"size": 2313
},
"fa": {
"name": "/icons/ic_antifeature_disabledalgorithm.xml",
"sha256": "94dea590c7c0aa37d351ab62a69fc7eefbc2cdbb84b79df3934c2e9332e1dcfb",
"size": 2313
},
"ro": {
"name": "/icons/ic_antifeature_disabledalgorithm.xml",
"sha256": "94dea590c7c0aa37d351ab62a69fc7eefbc2cdbb84b79df3934c2e9332e1dcfb",
"size": 2313
},
"zh-rCN": {
"name": "/icons/ic_antifeature_disabledalgorithm.xml",
"sha256": "94dea590c7c0aa37d351ab62a69fc7eefbc2cdbb84b79df3934c2e9332e1dcfb",
"size": 2313
}
},
"name": {
"de": "Mit einem unsicheren Algorithmus signiert",
"en-US": "Signed Using An Unsafe Algorithm",
"fa": "امضا شده با الگوریتمی ناامن",
"ro": "Algoritm nesigur semnătură",
"zh-rCN": "使用不安全算法签名"
}
},
"KnownVuln": {
"description": {
"de": "Diese App enthält eine bekannte Sicherheitslücke",
"en-US": "This app contains a known security vulnerability",
"fa": "این کاره، آسیب‌پذیری امنیتی شناخته‌شده‌ای دارد",
"ro": "Aplicația conține o vulnerabilitate de securitate cunoscută",
"zh-rCN": "此应用包含已知的安全漏洞"
},
"icon": {
"de": {
"name": "/icons/ic_antifeature_knownvuln.xml",
"sha256": "743ddcad0120896b03bf62bca9b3b9902878ac9366959a0b77b2c50beeb37f9d",
"size": 1415
},
"en-US": {
"name": "/icons/ic_antifeature_knownvuln.xml",
"sha256": "743ddcad0120896b03bf62bca9b3b9902878ac9366959a0b77b2c50beeb37f9d",
"size": 1415
},
"fa": {
"name": "/icons/ic_antifeature_knownvuln.xml",
"sha256": "743ddcad0120896b03bf62bca9b3b9902878ac9366959a0b77b2c50beeb37f9d",
"size": 1415
},
"ro": {
"name": "/icons/ic_antifeature_knownvuln.xml",
"sha256": "743ddcad0120896b03bf62bca9b3b9902878ac9366959a0b77b2c50beeb37f9d",
"size": 1415
},
"zh-rCN": {
"name": "/icons/ic_antifeature_knownvuln.xml",
"sha256": "743ddcad0120896b03bf62bca9b3b9902878ac9366959a0b77b2c50beeb37f9d",
"size": 1415
}
},
"name": {
"de": "Bekannte Sicherheitslücke",
"en-US": "Known Vulnerability",
"fa": "آسیب‌پذیری شناخته",
"ro": "Vulnerabilitate cunoscută",
"zh-rCN": "含有已知漏洞"
}
},
"NSFW": {
"description": {
"de": "Diese App enthält Inhalte, die nicht überall veröffentlicht oder sichtbar sein sollten",
"en-US": "This app contains content that should not be publicized or visible everywhere",
"fa": "این کاره محتوایی دارد که نباید عمومی شده یا همه‌حا نمایان باشد",
"ro": "Această aplicație conține conținut care nu ar trebui să fie făcut public sau vizibil peste tot",
"zh-rCN": "此应用包含不应宣扬或随处可见的内容"
},
"icon": {
"de": {
"name": "/icons/ic_antifeature_nsfw.xml",
"sha256": "acab2a7a846700529cd7f2b7a7980f7d04a291f22db8434f3e966f7350ed1465",
"size": 871
},
"en-US": {
"name": "/icons/ic_antifeature_nsfw.xml",
"sha256": "acab2a7a846700529cd7f2b7a7980f7d04a291f22db8434f3e966f7350ed1465",
"size": 871
},
"fa": {
"name": "/icons/ic_antifeature_nsfw.xml",
"sha256": "acab2a7a846700529cd7f2b7a7980f7d04a291f22db8434f3e966f7350ed1465",
"size": 871
},
"ro": {
"name": "/icons/ic_antifeature_nsfw.xml",
"sha256": "acab2a7a846700529cd7f2b7a7980f7d04a291f22db8434f3e966f7350ed1465",
"size": 871
},
"zh-rCN": {
"name": "/icons/ic_antifeature_nsfw.xml",
"sha256": "acab2a7a846700529cd7f2b7a7980f7d04a291f22db8434f3e966f7350ed1465",
"size": 871
}
},
"name": {
"de": "NSFW",
"en-US": "NSFW",
"fa": "NSFW",
"ro": "NSFW",
"zh-rCN": "NSFW"
}
},
"NoSourceSince": {
"description": {
"en-US": "The source code is no longer available, no updates possible."
},
"icon": {
"de": {
"name": "/icons/ic_antifeature_nosourcesince.xml",
"sha256": "69c880b075967fe9598c777e18d600e1c1612bf061111911421fe8f6b9d88d4f",
"size": 1102
},
"en-US": {
"name": "/icons/ic_antifeature_nosourcesince.xml",
"sha256": "69c880b075967fe9598c777e18d600e1c1612bf061111911421fe8f6b9d88d4f",
"size": 1102
},
"fa": {
"name": "/icons/ic_antifeature_nosourcesince.xml",
"sha256": "69c880b075967fe9598c777e18d600e1c1612bf061111911421fe8f6b9d88d4f",
"size": 1102
},
"ro": {
"name": "/icons/ic_antifeature_nosourcesince.xml",
"sha256": "69c880b075967fe9598c777e18d600e1c1612bf061111911421fe8f6b9d88d4f",
"size": 1102
},
"zh-rCN": {
"name": "/icons/ic_antifeature_nosourcesince.xml",
"sha256": "69c880b075967fe9598c777e18d600e1c1612bf061111911421fe8f6b9d88d4f",
"size": 1102
}
},
"name": {
"de": "Der Quellcode ist nicht mehr erhältlich, keine Aktualisierungen möglich.",
"en-US": "Newer Source Not Available",
"fa": "کد مبدأ دیگر در دسترس نیست. به‌روز رسانی ناممکن است.",
"ro": "Codul sursă nu mai este disponibil, nu mai există posibilitatea de a actualiza.",
"zh-rCN": "源代码不再可用,无法更新。"
}
},
"NonFreeAdd": {
"description": {
"de": "Diese App bewirbt nicht-quelloffene Erweiterungen",
"en-US": "This app promotes non-free add-ons",
"fa": "این کاره، افزونه‌های ناآزاد را تبلیغ می‌کند",
"ro": "Aplicația promovează anexe ce nu sunt software liber",
"zh-rCN": "此应用推广非自由的附加组件"
},
"icon": {
"de": {
"name": "/icons/ic_antifeature_nonfreeadd.xml",
"sha256": "a1d1f2070bdaabf80ca5a55bccef98c82031ea2f31cc040be5ec009f44ddeef2",
"size": 1846
},
"en-US": {
"name": "/icons/ic_antifeature_nonfreeadd.xml",
"sha256": "a1d1f2070bdaabf80ca5a55bccef98c82031ea2f31cc040be5ec009f44ddeef2",
"size": 1846
},
"fa": {
"name": "/icons/ic_antifeature_nonfreeadd.xml",
"sha256": "a1d1f2070bdaabf80ca5a55bccef98c82031ea2f31cc040be5ec009f44ddeef2",
"size": 1846
},
"ro": {
"name": "/icons/ic_antifeature_nonfreeadd.xml",
"sha256": "a1d1f2070bdaabf80ca5a55bccef98c82031ea2f31cc040be5ec009f44ddeef2",
"size": 1846
},
"zh-rCN": {
"name": "/icons/ic_antifeature_nonfreeadd.xml",
"sha256": "a1d1f2070bdaabf80ca5a55bccef98c82031ea2f31cc040be5ec009f44ddeef2",
"size": 1846
}
},
"name": {
"de": "Nicht-quelloffene Erweiterungen",
"en-US": "Non-Free Addons",
"fa": "افزونه‌های ناآزاد",
"ro": "Anexe ne-libere",
"zh-rCN": "非自由附加组件"
}
},
"NonFreeAssets": {
"description": {
"de": "Diese App enthält nicht-quelloffene Bestandteile",
"en-US": "This app contains non-free assets",
"fa": "این کاره دارای بخش‌های ناآزاد است",
"ro": "Aceasta aplicație conține resurse ce nu sunt la disponibile la liber",
"zh-rCN": "此应用包含非自由资源"
},
"icon": {
"de": {
"name": "/icons/ic_antifeature_nonfreeassets.xml",
"sha256": "b39fe384386fc67fb30fa2f91402594110e2e42c961d76adc93141b8bd774008",
"size": 1784
},
"en-US": {
"name": "/icons/ic_antifeature_nonfreeassets.xml",
"sha256": "b39fe384386fc67fb30fa2f91402594110e2e42c961d76adc93141b8bd774008",
"size": 1784
},
"fa": {
"name": "/icons/ic_antifeature_nonfreeassets.xml",
"sha256": "b39fe384386fc67fb30fa2f91402594110e2e42c961d76adc93141b8bd774008",
"size": 1784
},
"ro": {
"name": "/icons/ic_antifeature_nonfreeassets.xml",
"sha256": "b39fe384386fc67fb30fa2f91402594110e2e42c961d76adc93141b8bd774008",
"size": 1784
},
"zh-rCN": {
"name": "/icons/ic_antifeature_nonfreeassets.xml",
"sha256": "b39fe384386fc67fb30fa2f91402594110e2e42c961d76adc93141b8bd774008",
"size": 1784
}
},
"name": {
"de": "Nicht-quelloffene Bestandteile",
"en-US": "Non-Free Assets",
"fa": "بخش‌های ناآزاد",
"ro": "Resurse ne-libere",
"zh-rCN": "非自由资产"
}
},
"NonFreeDep": {
"description": {
"de": "Diese App ist abhängig von anderen nicht-quelloffenen Apps",
"en-US": "This app depends on other non-free apps",
"fa": "این کاره به دیگر کاره‌های ناآزاد وابسته است",
"ro": "Aplicația depinde de alte aplicații ce nu sunt software liber",
"zh-rCN": "此应用依赖于其它非自由应用"
},
"icon": {
"de": {
"name": "/icons/ic_antifeature_nonfreedep.xml",
"sha256": "c1b4052a8f58125b2120d9ca07adb725d47bfa7cfcea80c4d6bbbc432b5cb83a",
"size": 1396
},
"en-US": {
"name": "/icons/ic_antifeature_nonfreedep.xml",
"sha256": "c1b4052a8f58125b2120d9ca07adb725d47bfa7cfcea80c4d6bbbc432b5cb83a",
"size": 1396
},
"fa": {
"name": "/icons/ic_antifeature_nonfreedep.xml",
"sha256": "c1b4052a8f58125b2120d9ca07adb725d47bfa7cfcea80c4d6bbbc432b5cb83a",
"size": 1396
},
"ro": {
"name": "/icons/ic_antifeature_nonfreedep.xml",
"sha256": "c1b4052a8f58125b2120d9ca07adb725d47bfa7cfcea80c4d6bbbc432b5cb83a",
"size": 1396
},
"zh-rCN": {
"name": "/icons/ic_antifeature_nonfreedep.xml",
"sha256": "c1b4052a8f58125b2120d9ca07adb725d47bfa7cfcea80c4d6bbbc432b5cb83a",
"size": 1396
}
},
"name": {
"de": "Nicht-quelloffene Abhängigkeiten",
"en-US": "Non-Free Dependencies",
"fa": "وابستگی‌های ناآزاد",
"ro": "Dependențe ne-libere",
"zh-rCN": "非自由依赖项"
}
},
"NonFreeNet": {
"description": {
"de": "Diese App bewirbt nicht-quelloffene Netzwerkdienste",
"en-US": "This app promotes or depends entirely on a non-free network service",
"fa": "این کاره، خدمات شبکه‌های ناآزاد را ترویج می‌کند",
"ro": "Aplicația promovează servicii de rețea ce nu sunt accesibile la liber",
"zh-rCN": "此应用推广非自由的网络服务"
},
"icon": {
"de": {
"name": "/icons/ic_antifeature_nonfreenet.xml",
"sha256": "7fff45c847ed2ecc94e85ba2341685c8f113fa5fdf7267a25637dc38ee0275f6",
"size": 3038
},
"en-US": {
"name": "/icons/ic_antifeature_nonfreenet.xml",
"sha256": "7fff45c847ed2ecc94e85ba2341685c8f113fa5fdf7267a25637dc38ee0275f6",
"size": 3038
},
"fa": {
"name": "/icons/ic_antifeature_nonfreenet.xml",
"sha256": "7fff45c847ed2ecc94e85ba2341685c8f113fa5fdf7267a25637dc38ee0275f6",
"size": 3038
},
"ro": {
"name": "/icons/ic_antifeature_nonfreenet.xml",
"sha256": "7fff45c847ed2ecc94e85ba2341685c8f113fa5fdf7267a25637dc38ee0275f6",
"size": 3038
},
"zh-rCN": {
"name": "/icons/ic_antifeature_nonfreenet.xml",
"sha256": "7fff45c847ed2ecc94e85ba2341685c8f113fa5fdf7267a25637dc38ee0275f6",
"size": 3038
}
},
"name": {
"de": "Nicht-quelloffene Netzwerkdienste",
"en-US": "Non-Free Network Services",
"fa": "خدمات شبکه‌ای ناآزاد",
"ro": "Servicii de rețea ne-libere",
"zh-rCN": "非自由网络服务"
}
},
"Tracking": {
"description": {
"de": "Diese App verfolgt und versendet Ihre Aktivitäten",
"en-US": "This app tracks and reports your activity",
"fa": "این کاره، فعّالیتتان را ردیابی و گزارش می‌کند",
"ro": "Aplicația îți înregistrează și raportează activitatea undeva",
"zh-rCN": "此应用会记录并报告你的活动"
},
"icon": {
"de": {
"name": "/icons/ic_antifeature_tracking.xml",
"sha256": "4779337b5b0a12c4b4a8a83d0d8a994a2477460db702784df4c8d3e3730be961",
"size": 2493
},
"en-US": {
"name": "/icons/ic_antifeature_tracking.xml",
"sha256": "4779337b5b0a12c4b4a8a83d0d8a994a2477460db702784df4c8d3e3730be961",
"size": 2493
},
"fa": {
"name": "/icons/ic_antifeature_tracking.xml",
"sha256": "4779337b5b0a12c4b4a8a83d0d8a994a2477460db702784df4c8d3e3730be961",
"size": 2493
},
"ro": {
"name": "/icons/ic_antifeature_tracking.xml",
"sha256": "4779337b5b0a12c4b4a8a83d0d8a994a2477460db702784df4c8d3e3730be961",
"size": 2493
},
"zh-rCN": {
"name": "/icons/ic_antifeature_tracking.xml",
"sha256": "4779337b5b0a12c4b4a8a83d0d8a994a2477460db702784df4c8d3e3730be961",
"size": 2493
}
},
"name": {
"de": "Tracking",
"en-US": "Tracking",
"fa": "ردیابی",
"ro": "Urmărire",
"zh-rCN": "跟踪用户"
}
},
"UpstreamNonFree": {
"description": {
"de": "Der Originalcode ist nicht völlig quelloffen",
"en-US": "The upstream source code is not entirely Free",
"fa": "کد مبدأ بالادستی کاملاً آزاد نیست",
"ro": "Codul sursa originar nu este în totalitatea lui software liber",
"zh-rCN": "上游源代码不是完全自由的"
},
"icon": {
"de": {
"name": "/icons/ic_antifeature_upstreamnonfree.xml",
"sha256": "06a9af843ff56ecd7a270f98c0b19b3154edf3ffa854e6d50a84ef00d0ce1a86",
"size": 1442
},
"en-US": {
"name": "/icons/ic_antifeature_upstreamnonfree.xml",
"sha256": "06a9af843ff56ecd7a270f98c0b19b3154edf3ffa854e6d50a84ef00d0ce1a86",
"size": 1442
},
"fa": {
"name": "/icons/ic_antifeature_upstreamnonfree.xml",
"sha256": "06a9af843ff56ecd7a270f98c0b19b3154edf3ffa854e6d50a84ef00d0ce1a86",
"size": 1442
},
"ro": {
"name": "/icons/ic_antifeature_upstreamnonfree.xml",
"sha256": "06a9af843ff56ecd7a270f98c0b19b3154edf3ffa854e6d50a84ef00d0ce1a86",
"size": 1442
},
"zh-rCN": {
"name": "/icons/ic_antifeature_upstreamnonfree.xml",
"sha256": "06a9af843ff56ecd7a270f98c0b19b3154edf3ffa854e6d50a84ef00d0ce1a86",
"size": 1442
}
},
"name": {
"de": "Originalcode nicht-quelloffen",
"en-US": "Upstream Non-Free",
"fa": "بالادست ناآزاد",
"ro": "Surse ne-libere",
"zh-rCN": "上游代码非自由"
}
}
},
"requests": {
"install": [
"org.adaway"

View file

@ -9,15 +9,14 @@ import tempfile
import textwrap
from pathlib import Path
from testcommon import TmpCwd
from testcommon import TmpCwd, mkdtemp
localmodule = Path(__file__).resolve().parent.parent
print('localmodule: ' + str(localmodule))
if localmodule not in sys.path:
sys.path.insert(0, str(localmodule))
from fdroidserver import common
from fdroidserver import rewritemeta
from fdroidserver import common, metadata, rewritemeta
class RewriteMetaTest(unittest.TestCase):
@ -27,6 +26,123 @@ class RewriteMetaTest(unittest.TestCase):
logging.basicConfig(level=logging.DEBUG)
self.basedir = localmodule / 'tests'
os.chdir(self.basedir)
metadata.warnings_action = 'error'
self._td = mkdtemp()
self.testdir = self._td.name
def tearDown(self):
self._td.cleanup()
def test_remove_blank_flags_from_builds_com_politedroid_3(self):
"""Unset fields in Builds: entries should be removed."""
appid = 'com.politedroid'
app = metadata.read_metadata({appid: -1})[appid]
builds = rewritemeta.remove_blank_flags_from_builds(app.get('Builds'))
self.assertEqual(
builds[0],
{
'versionName': '1.2',
'versionCode': 3,
'commit': '6a548e4b19',
'target': 'android-10',
'antifeatures': [
'KnownVuln',
'UpstreamNonFree',
'NonFreeAssets',
],
},
)
def test_remove_blank_flags_from_builds_com_politedroid_4(self):
"""Unset fields in Builds: entries should be removed."""
appid = 'com.politedroid'
app = metadata.read_metadata({appid: -1})[appid]
builds = rewritemeta.remove_blank_flags_from_builds(app.get('Builds'))
self.assertEqual(
builds[1],
{
'versionName': '1.3',
'versionCode': 4,
'commit': 'ad865b57bf3ac59580f38485608a9b1dda4fa7dc',
'target': 'android-15',
},
)
def test_remove_blank_flags_from_builds_no_builds(self):
"""Unset fields in Builds: entries should be removed."""
self.assertEqual(
rewritemeta.remove_blank_flags_from_builds(None),
list(),
)
self.assertEqual(
rewritemeta.remove_blank_flags_from_builds(dict()),
list(),
)
def test_rewrite_no_builds(self):
os.chdir(self.testdir)
Path('metadata').mkdir()
with Path('metadata/a.yml').open('w') as f:
f.write('AutoName: a')
rewritemeta.main()
self.assertEqual(
Path('metadata/a.yml').read_text(encoding='utf-8'),
textwrap.dedent(
'''\
License: Unknown
AutoName: a
AutoUpdateMode: None
UpdateCheckMode: None
'''
),
)
def test_rewrite_empty_build_field(self):
os.chdir(self.testdir)
Path('metadata').mkdir()
with Path('metadata/a.yml').open('w') as fp:
fp.write(
textwrap.dedent(
"""
License: Apache-2.0
Builds:
- versionCode: 4
versionName: a
rm:
"""
)
)
rewritemeta.main()
self.assertEqual(
Path('metadata/a.yml').read_text(encoding='utf-8'),
textwrap.dedent(
'''\
License: Apache-2.0
Builds:
- versionName: a
versionCode: 4
AutoUpdateMode: None
UpdateCheckMode: None
'''
),
)
def test_remove_blank_flags_from_builds_app_with_special_build_params(self):
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[-1],
{
'versionName': '2.1.2',
'versionCode': 51,
'disable': 'Labelled as pre-release, so skipped',
},
)
def test_rewrite_scenario_trivial(self):
sys.argv = ['rewritemeta', 'a', 'b']

View file

@ -213,16 +213,6 @@ if use_apksigner; then
fi
#------------------------------------------------------------------------------#
echo_header "TODO remove once buildserver image is upgraded to bullseye with apksigner"
if java -version 2>&1 | grep -F 1.8.0; then
echo "Skipping the rest because they require apksigner 30.0.0+ which does not run on Java8"
echo SUCCESS
exit
fi
#------------------------------------------------------------------------------#
echo_header "test UTF-8 metadata"
@ -271,7 +261,12 @@ REPOROOT=`create_test_dir`
GNUPGHOME=$REPOROOT/gnupghome
cd $REPOROOT
fdroid_init_with_prebuilt_keystore
cp -a $WORKSPACE/tests/metadata $WORKSPACE/tests/repo $WORKSPACE/tests/stats $REPOROOT/
cp -a \
$WORKSPACE/tests/config \
$WORKSPACE/tests/metadata \
$WORKSPACE/tests/repo \
$WORKSPACE/tests/stats \
$REPOROOT/
cp -a $WORKSPACE/tests/gnupghome $GNUPGHOME
chmod 0700 $GNUPGHOME
echo "install_list: org.adaway" >> config.yml
@ -1139,7 +1134,7 @@ echo_header "setup a new repo from scratch using ANDROID_HOME with git mirror"
# fake git remote server for repo mirror
SERVER_GIT_MIRROR=`create_test_dir`
cd $SERVER_GIT_MIRROR
$git init
$git init --initial-branch=master
$git config receive.denyCurrentBranch updateInstead
REPOROOT=`create_test_dir`
@ -1201,7 +1196,7 @@ SERVERWEBROOT=`create_test_dir`/fdroid
cd $OFFLINE_ROOT
mkdir binary_transparency
cd binary_transparency
$git init
$git init --initial-branch=master
# fake git remote server for binary transparency log
BINARY_TRANSPARENCY_REMOTE=`create_test_dir`
@ -1209,7 +1204,7 @@ BINARY_TRANSPARENCY_REMOTE=`create_test_dir`
# fake git remote server for repo mirror
SERVER_GIT_MIRROR=`create_test_dir`
cd $SERVER_GIT_MIRROR
$git init
$git init --initial-branch=master
$git config receive.denyCurrentBranch updateInstead
cd $OFFLINE_ROOT