Compare commits

...

15 Commits

Author SHA1 Message Date
FestplattenSchnitzel 4135ef21cc Merge branch 'metadata_json_schema' into 'master'
Draft: Validate metadata with JSON schema

See merge request fdroid/fdroidserver!1063
2024-05-08 05:50:45 +00:00
Hans-Christoph Steiner 4666330a4d Merge branch 'gradlefile' into 'master'
throw error if gradle build method is used but no build.gradle file is found

See merge request fdroid/fdroidserver!1479
2024-05-07 14:14:26 +00:00
linsui 7104411296 throw error if gradle build method is used but no build.gradle file is found 2024-05-07 14:13:47 +00:00
Hans-Christoph Steiner 99bd544ab9 Merge branch 'fedora-40-ci-failure' into 'master'
make it easier to support the Fedora job

See merge request fdroid/fdroidserver!1474
2024-05-07 14:11:53 +00:00
Hans-Christoph Steiner 5df3d27126 gitlab-ci: stay on Fedora 39 until it is no longer supported
We can rely on the debian:testing job to test the bleeding edge, and it is
a lot easier to troubleshoot.

The Fedora job is a lot harder to troubleshoot than the Debian-based jobs,
and they are often quite bleeding edge.  Currently, there is a change to
either Python or an image processing lib (Pillow?) that now compresses PNGs
differently than all previous releases.  That breaks the tests based on
processing images and checking the SHA-256 matches.

70e7e720b9
fdroidserver!669
2024-05-07 12:58:23 +00:00
Hans-Christoph Steiner 1b65e33835 make it easy to keep test artifacts from jobs
When troubleshooting things that are difficult to reproduce locally, like
different behaviors in the fedora_latest job, these changes make it easy to
keep the test files around after the tests run.  For example, if PNGs are
processed differently by newer Python versions.
2024-05-07 12:58:23 +00:00
Hans-Christoph Steiner 299e3e5f4c index: handle image processing diffs across various Python versions
Apparently, the newest Python thingies strip the PNGs a tiny bit smaller,
so a fixed file size will lead to the test failing:

https://gitlab.com/fdroid/fdroidserver/-/jobs/6703386074
```
Traceback (most recent call last):
  File "/builds/fdroid/fdroidserver/tests/index.TestCase", line 704, in test_package_metadata
    self.assertEqual(36027, metadata['featureGraphic']['en-US']['size'])
AssertionError: 36027 != 35619
```
2024-05-07 12:58:23 +00:00
Hans-Christoph Steiner 1cb1394de3 Merge branch 'debugkey' into 'master'
lint: blocklist known AOSP debug keys in AASK

See merge request fdroid/fdroidserver!1478
2024-05-07 11:33:12 +00:00
Hans-Christoph Steiner 9a9b5beeaa simplify test setup
I'm in the midst of working towards getting rid of the "config" instances
that are in the subcommand module, e.g. `fdroidserver.lint.config`
2024-05-07 11:33:04 +00:00
Hans-Christoph Steiner 14c8647909 add additional tests 2024-05-07 11:33:04 +00:00
linsui d243cbd030 lint: blocklist known AOSP debug keys in AASK 2024-05-07 11:33:04 +00:00
Gregor Düster 91bad83ccf
fixup! lint: Validate metadata with JSON schema 2023-08-03 13:45:03 +02:00
Gregor Düster 8a1ca3259e
Remove tests around deprecated 'Provides' metadata field 2023-08-03 13:45:02 +02:00
Gregor Düster 5f1f37d402
Fix index.TestCase:test_package_metadata 2023-08-03 13:45:02 +02:00
Gregor Düster f7a7ad32eb
lint: Validate metadata with JSON schema
This is implemented in fdroidserver/metadata.py and can be enabled for
any other call to read_metadata in the future if it proofs sucessful.

Co-authored-by: Jochen Sprickerhof <gitlab@jochen.sprickerhof.de>
Co-authored-by: linsui <linsui555@gmail.com>
2023-08-03 13:45:01 +02:00
17 changed files with 681 additions and 104 deletions

View File

@ -284,10 +284,9 @@ black:
- apt-get install black
- black --check --diff --color $CI_PROJECT_DIR
fedora_latest:
image: fedora:latest
only:
- master@fdroid/fdroidserver
image: fedora:39 # support ends on 2024-11-12
script:
# tricks to hopefully make runs more reliable
- echo "timeout=600" >> /etc/dnf/dnf.conf

View File

@ -42,6 +42,7 @@ include locale/zh_Hans/LC_MESSAGES/fdroidserver.po
include locale/zh_Hant/LC_MESSAGES/fdroidserver.po
include makebuildserver
include README.md
include fdroidserver/schemas/metadata.json
include tests/aosp_testkey_debug.keystore
include tests/apk.embedded_1.apk
include tests/bad-unicode-*.apk
@ -724,6 +725,7 @@ include tests/repo/v1.v2.sig_1020.apk
include tests/rewritemeta.TestCase
include tests/run-tests
include tests/scanner.TestCase
include tests/schemas.TestCase
include tests/signatures.TestCase
include tests/signindex.TestCase
include tests/signindex/guardianproject.jar

View File

@ -2354,6 +2354,8 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
gradlefile = build_gradle
elif os.path.exists(build_gradle_kts):
gradlefile = build_gradle_kts
else:
raise BuildException("No gradle file found")
regsub_file(r'compileSdkVersion[ =]+[0-9]+',
r'compileSdkVersion %s' % n,
gradlefile)

View File

@ -722,7 +722,13 @@ def check_updates_ucm_http_aum_pattern(app): # noqa: D403
def check_certificate_pinned_binaries(app):
if len(app.get('AllowedAPKSigningKeys')) > 0:
keys = app.get('AllowedAPKSigningKeys')
known_keys = common.config.get('apk_signing_key_block_list', [])
if keys:
if known_keys:
for key in keys:
if key in known_keys:
yield _('Known debug key is used in AllowedAPKSigningKeys: ') + key
return
if app.get('Binaries') is not None:
yield _(
@ -843,7 +849,7 @@ def main():
def lint_metadata(options):
# Get all apps...
allapps = metadata.read_metadata(options.appid)
allapps = metadata.read_metadata(options.appid, validate_with_schema=True)
apps = common.read_app_args(options.appid, allapps, False)
anywarns = check_for_unsupported_metadata_files()

View File

@ -19,6 +19,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import git
import importlib
import json
from pathlib import Path
import math
import platform
@ -28,9 +30,15 @@ import logging
import ruamel.yaml
from collections import OrderedDict
try:
import jsonschema
except ImportError:
jsonschema = None
from . import common
from . import _
from .exception import MetaDataException
from . import schemas
srclibs = None
warnings_action = None
@ -526,6 +534,15 @@ def parse_yaml_srclib(metadatapath):
return thisinfo
def get_validator():
schema = json.loads(
importlib.resources.files(schemas)
.joinpath('metadata.json')
.read_text(encoding='utf-8')
)
return jsonschema.validators.validator_for(schema)(schema)
def read_srclibs():
"""Read all srclib metadata.
@ -551,7 +568,7 @@ def read_srclibs():
srclibs[metadatapath.stem] = parse_yaml_srclib(metadatapath)
def read_metadata(appids={}, sort_by_time=False):
def read_metadata(appids={}, sort_by_time=False, validate_with_schema=False):
"""Return a list of App instances sorted newest first.
This reads all of the metadata files in a 'data' repository, then
@ -562,6 +579,13 @@ def read_metadata(appids={}, sort_by_time=False):
appids is a dict with appids a keys and versionCodes as values.
"""
validator = None
if validate_with_schema:
if jsonschema:
validator = get_validator()
else:
logging.warning('Validation with JSON schema requested but jsonschema (python3-jsonschema) is not installed!')
# Always read the srclibs before the apps, since they can use a srlib as
# their source repository.
read_srclibs()
@ -600,14 +624,19 @@ def read_metadata(appids={}, sort_by_time=False):
_warn_or_exception(
_("Found multiple metadata files for {appid}").format(appid=appid)
)
app = parse_metadata(metadatapath)
app = parse_metadata(metadatapath, validator=validator)
# app might be None if schema validation failed, but only a warning was raised
if not app:
continue
check_metadata(app)
apps[app.id] = app
return apps
def parse_metadata(metadatapath):
def parse_metadata(metadatapath, validator=None):
"""Parse metadata file, also checking the source repo for .fdroid.yml.
This function finds the relevant files, gets them parsed, converts
@ -628,9 +657,11 @@ def parse_metadata(metadatapath):
Parameters
----------
metadatapath
metadatapath: Path
The file path to read. The "Application ID" aka "Package Name"
for the application comes from this filename.
validator: jsonschema.Validator
The validator for the parsed data from the metadatapath.
Raises
------
@ -651,7 +682,12 @@ def parse_metadata(metadatapath):
app.metadatapath = metadatapath.as_posix()
if metadatapath.suffix == '.yml':
with metadatapath.open('r', encoding='utf-8') as mf:
app.update(parse_yaml_metadata(mf))
_app = parse_yaml_metadata(mf, validator=validator)
if _app is not None:
app.update(_app)
else:
# Schema validation failed
return None
else:
_warn_or_exception(
_('Unknown metadata format: {path} (use: *.yml)').format(path=metadatapath)
@ -676,8 +712,7 @@ def parse_metadata(metadatapath):
logging.debug(
_('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():
for k, v in (parse_metadata(metadata_in_repo) or {}).items():
if k not in app:
app[k] = v
@ -701,7 +736,7 @@ def parse_metadata(metadatapath):
return app
def parse_yaml_metadata(mf):
def parse_yaml_metadata(mf, validator=None):
"""Parse the .yml file and post-process it.
This function handles parsing a metadata YAML file and converting
@ -714,6 +749,15 @@ def parse_yaml_metadata(mf):
is post processing. That makes the parsing perform something like
Strict YAML.
Parameters
----------
mf : io.TextIOWrapper
Buffered text stream of the metadata file.
app : App
The App instance to put the data in.
validator : jsonschema.Validator
Validator to validate the data from the mf.
"""
try:
yaml = ruamel.yaml.YAML(typ='safe')
@ -739,6 +783,17 @@ def parse_yaml_metadata(mf):
)
yamldata = dict()
if validator:
# Validate using JSON schema validator and handle errors
errors = list(validator.iter_errors(yamldata))
if errors:
_warn_or_exception(
_('%s is not valid:%s') % (
mf.name, '\n' + '\n'.join(e.message for e in errors)
)
)
return None
deprecated_in_yaml = ['Provides']
for field in tuple(yamldata.keys()):

View File

View File

@ -0,0 +1,418 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://f-droid.org/metadata-generic.yml",
"title": "F-Droid metadata for Android apps",
"description": "For every app on F-Droid a metadata file in the format of <application-id>.yml have to be provided, in order to build an app or check or if it has been updated. See https://f-droid.org/docs/Build_Metadata_Reference for a complete reference of a metadata file.",
"type": "object",
"properties": {
"AntiFeatures": {
"description": "Any number of anti-features the application has.",
"$ref": "#/definitions/arrayOfString",
"minItems": 1
},
"Categories": {
"description": "Any number of categories for the application to be placed in.",
"$ref": "#/definitions/arrayOfString",
"minItems": 1
},
"License": {
"description": "(Main) Licence of the app's source code.",
"type": "string"
},
"AuthorName": {
"description": "The name of the author, either full, abbreviated or pseudonym.",
"type": "string"
},
"AuthorEmail": {
"description": "The e-mail address of the author(s).",
"type": "string"
},
"AuthorWebSite": {
"description": "The website url of the author(s).",
"type": "string"
},
"WebSite": {
"description": "The URL for the application's web site.",
"type": "string"
},
"SourceCode": {
"description": "The URL to view or obtain the application's source code. This should be something human-friendly.",
"type": "string"
},
"IssueTracker": {
"description": "The URL for the application's issue tracker.",
"type": "string"
},
"Translation": {
"description": "The URL for the application's translation portal or at least a guide.",
"type": "string"
},
"Changelog": {
"description": "The URL for the application's changelog.",
"type": "string"
},
"Donate": {
"description": "The URL to donate to the project. This should be the project's donate page if it has one.",
"type": "string"
},
"FlattrID": {
"description": "The project's Flattr (https://flattr.com) ID, if it has one.",
"type": "string"
},
"Liberapay": {
"description": "The project's Liberapay (https://liberapay.com) user or group name, if it has one.",
"type": "string"
},
"LiberapayID": {
"description": "The project's Liberapay (https://liberapay.com) user or group ID, if it has one.",
"type": "string"
},
"OpenCollective": {
"description": "The project's OpenCollective (https://opencollective.com) user or group name, if it has one.",
"type": "string"
},
"Bitcoin": {
"description": "A bitcoin address for donating to the project.",
"type": "string"
},
"Litecoin": {
"description": "A litecoin address for donating to the project.",
"type": "string"
},
"Name": {
"description": "The title of the application, with optional descriptive phrase.",
"type": "string"
},
"AutoName": {
"description": "The name of the application as can best be retrieved from the source code.",
"type": "string"
},
"Summary": {
"description": "DEPRECATED. A brief summary of what the application is. Should rather be provided via Fastlane.",
"type": "string",
"maxLength": 80
},
"Description": {
"description": "DEPRECATED. A full description of the application, relevant to the latest version. Should rather be provided via Fastlane.",
"type": "string",
"maxLength": 4000
},
"MaintainerNotes": {
"description": "This is a multi-line field using the same rules and syntax as the description. It's used to record notes for F-Droid maintainers to assist in maintaining and updating the application in the repository.",
"type": "string",
"maxLength": 4000
},
"RepoType": {
"description": "The type of repository - for automatic building from source.",
"enum": [
"git",
"svn",
"git-svn",
"hg",
"bzr",
"srclib"
]
},
"Repo": {
"description": "The repository location. Usually a git: or svn: URL, for example.",
"type": "string"
},
"Binaries": {
"description": "The location of binaries used in verification process.",
"type": "string"
},
"Builds": {
"description": "Any number of sub-entries can be present, each specifying a version to automatically build from source",
"type": "array",
"items": {
"type": "object",
"properties": {
"versionName": {
"description": "The version name of the build.",
"type": "string"
},
"versionCode": {
"description": "The version code of the build.",
"type": "integer"
},
"commit": {
"description": "The commit parameter specifies the tag, commit or revision number from which to build it in the source repository.",
"type": "string"
},
"disable": {
"description": "Disables this build, giving a reason why. (For backwards compatibility, this can also be achieved by starting the commit ID with '!')",
"type": "string"
},
"subdir": {
"description": "Specifies to build from a subdirectory of the checked out source code. Normally this directory is changed to before building.",
"type": "string"
},
"submodules": {
"description": "Use if the project (git only) has submodules - causes git submodule update --init --recursive to be executed after the source is cloned.",
"enum": [true]
},
"sudo": {
"description": "Specifies commands to be run using sudo bash -x -c \"xxxx\" in the buildserver VM guest. This commands are run with full root privileges, but the state will be reset after each build.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"timeout": {
"description": "Time limit for this build (in seconds). If the time is up, buildserver VM is forcefully terminated. The default is 7200 (2 hours); 0 means no limit.",
"type": "integer"
},
"init": {
"description": "Like 'prebuild', but runs on the source code before any other processing takes place.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"oldsdkloc": {
"description": "The sdk location in the repo is in an old format, or the build.xml is expecting such. The 'new' format is sdk.dir while the very old format is sdk-location.",
"enum": [true]
},
"target": {
"description": "Specifies a particular SDK target for compilation, overriding the value defined in the code by upstream. This has different effects depending on what build system used — this flag currently affects Ant, Maven and Gradle projects only.",
"type": "string"
},
"androidupdate": {
"description": "By default, 'android update' is used in Ant builds to generate or update the project and all its referenced projects. Specifying update=no bypasses that. Note that this is useless in builds that don't use Ant.",
"oneOf": [
{
"enum": ["auto"]
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"encoding": {
"description": "Adds a java.encoding property to local.properties with the given value. Generally the value will be 'utf-8'.",
"type": "string"
},
"forceversion": {
"description": "If specified, the package version in AndroidManifest.xml is replaced with the version name for the build as specified in the metadata.",
"enum": [true]
},
"forcevercode": {
"description": "If specified, the package version code in the AndroidManifest.xml is replaced with the version code for the build. See also forceversion.",
"enum": [true]
},
"rm": {
"description": "Specifies the relative paths of files or directories to delete before the build is done.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"extlibs": {
"description": "Comma-separated list of external libraries (jar files) from the build/extlib library, which will be placed in the libs directory of the project.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"srclibs": {
"description": "Comma-separated list of source libraries or Android projects. Each item is of the form name@rev where name is the predefined source library name and rev is the revision or tag to use in the respective source control.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"patch": {
"description": "Apply patch(es). 'x' names one (or more - comma-separated) files within a directory below the metadata, with the same name as the metadata file but without the extension. Each of these patches is applied to the code in turn.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"prebuild": {
"description": "Specifies a shell command (or commands - chain with &&) to run before the build takes place.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"scanignore": {
"description": "Enables one or more files/paths to be excluded from the scan process. This should only be used where there is a very good reason, and probably accompanied by a comment explaining why it is necessary.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"scandelete": {
"description": "When running the scan process, any files that trigger errors - like binaries - will be removed. It acts just like scanignore, but instead of ignoring the files, it removes them.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"build": {
"description": "As for 'prebuild', but runs during the actual build phase (but before the main Ant/Maven build). Use this only for actions that do actual building. Any preparation of the source code should be done using 'init' or 'prebuild'.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"buildjni": {
"description": "Enables building of native code via the ndk-build script before doing the main Ant build.",
"oneOf": [
{
"type": "string",
"enum": ["yes", "no"]
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"ndk": {
"description": "Version of the NDK to use in this build.",
"type": "string"
},
"gradle": {
"description": "Build with Gradle instead of Ant, specifying what flavours to use. Flavours are case sensitive since the path to the output APK is as well.",
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"enum": ["yes"]
},
"maxItems": 1
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"maven": {
"description": "Build with Maven instead of Ant. An extra @<dir> tells F-Droid to run Maven inside that relative subdirectory.",
"anyOf": [
{
"type": "string"
},
{
"type": "string",
"enum": ["yes", "yes@"]
}
]
},
"preassemble": {
"description": "List of Gradle tasks to be run before the assemble task in a Gradle project build.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"gradleprops": {
"description": "List of Gradle properties to pass via the command line to Gradle. A property can be of the form foo or of the form key=value.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"antcommands": {
"description": "Specify an alternate set of Ant commands (target) instead of the default 'release'. It can't be given any flags, such as the path to a build.xml.",
"$ref": "#/definitions/arrayOfStringOrString"
},
"output": {
"description": "Specify a glob path where the resulting unsigned release APK from the build should be.",
"type": "string"
},
"novcheck": {
"description": "Don't check that the version name and code in the resulting APK are correct by looking at the build output - assume the metadata is correct.",
"enum": [true]
},
"antifeatures": {
"description": "List of Anti-Features for this specific build. They are described in AntiFeatures.",
"$ref": "#/definitions/arrayOfString"
}
}
},
"minItems": 1
},
"Disabled": {
"description": "If this field is present, the application does not get put into the public index. The value should be a description of why the application is disabled.",
"type": "string"
},
"RequiresRoot": {
"description": "Set this optional field to “Yes” if the application requires root privileges to be usable.",
"enum": [
"True",
"yes"
]
},
"ArchivePolicy": {
"description": "This determines the policy for moving old versions of an app to the archive repo, if one is configured. Currently the only supported format is “n versions”, where n is the number of versions to keep. Defaults to “3 versions”.",
"type": "string",
"pattern": "^[0-9]+ versions$"
},
"AutoUpdateMode": {
"description": "This determines the method used for auto-generating new builds when new releases are available - in other words, adding a new Build Version line to the metadata.",
"anyOf": [
{
"type": "string"
},
{
"enum": ["None"]
}
]
},
"UpdateCheckMode": {
"description": "This determines the method using for determining when new releases are available - in other words, the updating of the CurrentVersion and CurrentVersionCode fields in the metadata by the fdroid checkupdates process.",
"type": "string",
"anyOf": [
{
"type": "string"
},
{
"enum": [
"None",
"Static",
"RepoManifest",
"RepoTrunk",
"Tags",
"HTTP"
]
}
]
},
"VercodeOperation": {
"description": "A list of operations to be applied to the versionCode obtained by the defined UpdateCheckMode. %c will be replaced by the actual vercode, and each string will be passed to python's eval function to calculate a version code.",
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true,
"minItems": 1
},
"UpdateCheckIgnore": {
"description": "When checking for updates (via UpdateCheckMode) this can be used to specify a regex which, if matched against the version name, causes that version to be ignored.",
"type": "string"
},
"UpdateCheckName": {
"description": "When checking for updates (via UpdateCheckMode) this can be used to specify the package name to search for. Useful when apps have a static package name but change it programmatically in some app flavors, by e.g. appending “.open” or “.free” at the end of the package name.",
"anyOf": [
{
"type": "string"
},
{
"enum": ["Ignore"]
}
]
},
"UpdateCheckData": {
"description": "Used in conjunction with UpdateCheckMode for certain modes.",
"type": "string"
},
"CurrentVersion": {
"description": "The name of the version that is the recommended release. There may be newer versions of the application than this (e.g. unstable versions), and there will almost certainly be older ones.",
"type": "string"
},
"CurrentVersionCode": {
"description": "The version code corresponding to the CurrentVersion field.",
"type": "integer"
},
"NoSourceSince": {
"description": "In case we are missing the source code for the CurrentVersion reported by Upstream, or that Non-Free elements have been introduced, this defines the first version that began to miss source code.",
"type": "string"
}
},
"definitions": {
"arrayOfStringOrString": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"arrayOfString": {
"type": "array",
"items": {
"type": "string"
}
}
}
}

View File

@ -79,7 +79,12 @@ setup(
author_email='team@f-droid.org',
url='https://f-droid.org',
license='AGPL-3.0',
packages=['fdroidserver', 'fdroidserver.asynchronousfilereader'],
packages=[
'fdroidserver',
'fdroidserver.asynchronousfilereader',
'fdroidserver.schemas',
],
include_package_data=True,
entry_points={'console_scripts': ['fdroid=fdroidserver.__main__:main']},
data_files=get_data_files(),
python_requires='>=3.9',
@ -96,6 +101,7 @@ setup(
'clint',
'defusedxml',
'GitPython',
'jsonschema',
'paramiko',
'Pillow',
'apache-libcloud >= 0.14.1',

View File

@ -701,8 +701,14 @@ class IndexTest(unittest.TestCase):
app = apps[appid]
metadata = index.package_metadata(app, 'repo')
# files
self.assertEqual(36027, metadata['featureGraphic']['en-US']['size'])
self.assertEqual(1413, metadata['icon']['en-US']['size'])
self.assertEqual(
os.path.getsize(f'repo/{appid}/en-US/featureGraphic.png'),
metadata['featureGraphic']['en-US']['size'],
)
self.assertEqual(
os.path.getsize(f'repo/{appid}/en-US/icon.png'),
metadata['icon']['en-US']['size'],
)
# localized strings
self.assertEqual({'en-US': 'title'}, metadata['name'])
self.assertEqual({'en-US': 'video'}, metadata['video'])

View File

@ -438,6 +438,45 @@ class LintTest(unittest.TestCase):
with self.assertRaises(TypeError):
fdroidserver.lint.lint_config('mirrors.yml')
def test_check_certificate_pinned_binaries_empty(self):
fdroidserver.common.config = {}
app = fdroidserver.metadata.App()
app.AllowedAPKSigningKeys = [
'a40da80a59d170caa950cf15c18c454d47a39b26989d8b640ecd745ba71bf5dc'
]
self.assertEqual(
[],
list(fdroidserver.lint.check_certificate_pinned_binaries(app)),
"when the config is empty, any signing key should be allowed",
)
def test_lint_known_debug_keys_no_match(self):
fdroidserver.common.config = {
"apk_signing_key_block_list": "a40da80a59d170caa950cf15c18c454d47a39b26989d8b640ecd745ba71bf5dc"
}
app = fdroidserver.metadata.App()
app.AllowedAPKSigningKeys = [
'2fd4fd5f54babba4bcb21237809bb653361d0d2583c80964ec89b28a26e9539e'
]
self.assertEqual(
[],
list(fdroidserver.lint.check_certificate_pinned_binaries(app)),
"A signing key that does not match one in the config should be allowed",
)
def test_lint_known_debug_keys(self):
fdroidserver.common.config = {
'apk_signing_key_block_list': 'a40da80a59d170caa950cf15c18c454d47a39b26989d8b640ecd745ba71bf5dc'
}
app = fdroidserver.metadata.App()
app.AllowedAPKSigningKeys = [
'a40da80a59d170caa950cf15c18c454d47a39b26989d8b640ecd745ba71bf5dc'
]
for warn in fdroidserver.lint.check_certificate_pinned_binaries(app):
anywarns = True
logging.debug(warn)
self.assertTrue(anywarns)
class LintAntiFeaturesTest(unittest.TestCase):
def setUp(self):

View File

@ -919,27 +919,6 @@ class MetadataTest(unittest.TestCase):
},
)
def test_parse_yaml_provides_should_be_ignored(self):
mf = io.StringIO(
textwrap.dedent(
"""\
Provides: this.is.deprecated
AutoName: F-Droid
RepoType: git
Builds:
- versionCode: 1
versionName: v0.1.0
prebuild: |-
a && b && sed -i 's,a,b,'
"""
)
)
mf.name = 'mock_filename.yaml'
mf.seek(0)
result = fdroidserver.metadata.parse_yaml_metadata(mf)
self.assertNotIn('Provides', result)
self.assertNotIn('provides', result)
def test_parse_yaml_app_antifeatures_dict(self):
nonfreenet = 'free it!'
tracking = 'so many'
@ -1508,39 +1487,6 @@ class MetadataTest(unittest.TestCase):
),
)
def test_write_yaml_make_sure_provides_does_not_get_written(self):
mf = io.StringIO()
app = fdroidserver.metadata.App()
app.Categories = ['None']
app.Provides = 'this.is.deprecated'
app['Builds'] = []
build = fdroidserver.metadata.Build()
build.versionCode = 102030
build.versionName = 'v1.2.3'
build.gradle = ['yes']
app['Builds'].append(build)
fdroidserver.metadata.write_yaml(mf, app)
mf.seek(0)
self.assertEqual(
mf.read(),
textwrap.dedent(
"""\
Categories:
- None
License: Unknown
Builds:
- versionName: v1.2.3
versionCode: 102030
gradle:
- yes
AutoUpdateMode: None
UpdateCheckMode: None
"""
),
)
def test_parse_yaml_srclib_unknown_key(self):
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
with Path('test.yml').open('w', encoding='utf-8') as f:

View File

@ -5,8 +5,8 @@ OpenCollective: f-droid-just-testing
Categories:
- Development
- GuardianProject
- 1
- 2.0
- '1'
- '2.0'
CurrentVersionCode: 2147483647
AuthorWebSite: https://guardianproject.info
Description: |

View File

@ -3,7 +3,7 @@ Categories:
License: GPL-3.0-only
SourceCode: https://github.com/eighthave/urzip
Bitcoin: 1Fi5xUHiAPRKxHvyUGVFGt9extBe8Srdbk
Liberapay: 12334
Liberapay: '12334'
AutoName: OBB Main Old Version

View File

@ -381,7 +381,8 @@ Builds:
versionCode: 1010303
commit: 1.1.3
subdir: vlc-android
gradle: VanillaARMv6fpu
gradle:
- VanillaARMv6fpu
srclibs: VLC@a9b19e4
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -392,7 +393,8 @@ Builds:
versionCode: 1010304
commit: 1.1.3
subdir: vlc-android
gradle: VanillaARMv7
gradle:
- VanillaARMv7
srclibs: VLC@a9b19e4
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -403,7 +405,8 @@ Builds:
versionCode: 1010305
commit: 1.1.3
subdir: vlc-android
gradle: VanillaX86
gradle:
- VanillaX86
srclibs: VLC@a9b19e4
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -414,7 +417,8 @@ Builds:
versionCode: 1010503
commit: 1.1.5
subdir: vlc-android
gradle: VanillaARMv6fpu
gradle:
- VanillaARMv6fpu
srclibs: VLC@e6b4585
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -425,7 +429,8 @@ Builds:
versionCode: 1010504
commit: 1.1.5
subdir: vlc-android
gradle: VanillaARMv7
gradle:
- VanillaARMv7
srclibs: VLC@e6b4585
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -436,7 +441,8 @@ Builds:
versionCode: 1010505
commit: 1.1.5
subdir: vlc-android
gradle: VanillaX86
gradle:
- VanillaX86
srclibs: VLC@e6b4585
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -447,7 +453,8 @@ Builds:
versionCode: 1010603
commit: 1.1.6
subdir: vlc-android
gradle: VanillaARMv6fpu
gradle:
- VanillaARMv6fpu
srclibs: VLC@551b670
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -458,7 +465,8 @@ Builds:
versionCode: 1010604
commit: 1.1.6
subdir: vlc-android
gradle: VanillaARMv7
gradle:
- VanillaARMv7
srclibs: VLC@551b670
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -469,7 +477,8 @@ Builds:
versionCode: 1010605
commit: 1.1.6
subdir: vlc-android
gradle: VanillaX86
gradle:
- VanillaX86
srclibs: VLC@551b670
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -480,7 +489,8 @@ Builds:
versionCode: 1020003
commit: 1.2.0
subdir: vlc-android
gradle: VanillaARMv6fpu
gradle:
- VanillaARMv6fpu
srclibs: VLC@23c8d86
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -491,7 +501,8 @@ Builds:
versionCode: 1020004
commit: 1.2.0
subdir: vlc-android
gradle: VanillaARMv7
gradle:
- VanillaARMv7
srclibs: VLC@23c8d86
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -502,7 +513,8 @@ Builds:
versionCode: 1020005
commit: 1.2.0
subdir: vlc-android
gradle: VanillaX86
gradle:
- VanillaX86
srclibs: VLC@23c8d86
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -513,7 +525,8 @@ Builds:
versionCode: 1020103
commit: 1.2.1
subdir: vlc-android
gradle: VanillaARMv6fpu
gradle:
- VanillaARMv6fpu
srclibs: VLC@23c8d86
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -524,7 +537,8 @@ Builds:
versionCode: 1020104
commit: 1.2.1
subdir: vlc-android
gradle: VanillaARMv7
gradle:
- VanillaARMv7
srclibs: VLC@23c8d86
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -535,7 +549,8 @@ Builds:
versionCode: 1020105
commit: 1.2.1
subdir: vlc-android
gradle: VanillaX86
gradle:
- VanillaX86
srclibs: VLC@23c8d86
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -546,7 +561,8 @@ Builds:
versionCode: 1020203
commit: 1.2.2
subdir: vlc-android
gradle: VanillaARMv6fpu
gradle:
- VanillaARMv6fpu
srclibs: VLC@7491a5f
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -557,7 +573,8 @@ Builds:
versionCode: 1020204
commit: 1.2.2
subdir: vlc-android
gradle: VanillaARMv7
gradle:
- VanillaARMv7
srclibs: VLC@7491a5f
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -568,7 +585,8 @@ Builds:
versionCode: 1020205
commit: 1.2.2
subdir: vlc-android
gradle: VanillaX86
gradle:
- VanillaX86
srclibs: VLC@7491a5f
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -579,7 +597,8 @@ Builds:
versionCode: 1020303
commit: 1.2.3
subdir: vlc-android
gradle: VanillaARMv6fpu
gradle:
- VanillaARMv6fpu
srclibs: VLC@7491a5f
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -590,7 +609,8 @@ Builds:
versionCode: 1020304
commit: 1.2.3
subdir: vlc-android
gradle: VanillaARMv7
gradle:
- VanillaARMv7
srclibs: VLC@7491a5f
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -601,7 +621,8 @@ Builds:
versionCode: 1020305
commit: 1.2.3
subdir: vlc-android
gradle: VanillaX86
gradle:
- VanillaX86
srclibs: VLC@7491a5f
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -612,7 +633,8 @@ Builds:
versionCode: 1020403
commit: 1.2.4
subdir: vlc-android
gradle: VanillaARMv6fpu
gradle:
- VanillaARMv6fpu
srclibs: VLC@7491a5f
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -623,7 +645,8 @@ Builds:
versionCode: 1020404
commit: 1.2.4
subdir: vlc-android
gradle: VanillaARMv7
gradle:
- VanillaARMv7
srclibs: VLC@7491a5f
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -634,7 +657,8 @@ Builds:
versionCode: 1020405
commit: 1.2.4
subdir: vlc-android
gradle: VanillaX86
gradle:
- VanillaX86
srclibs: VLC@7491a5f
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -645,7 +669,8 @@ Builds:
versionCode: 1020503
commit: 1.2.5
subdir: vlc-android
gradle: VanillaARMv6fpu
gradle:
- VanillaARMv6fpu
srclibs: VLC@50accb8
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -656,7 +681,8 @@ Builds:
versionCode: 1020504
commit: 1.2.5
subdir: vlc-android
gradle: VanillaARMv7
gradle:
- VanillaARMv7
srclibs: VLC@50accb8
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -667,7 +693,8 @@ Builds:
versionCode: 1020505
commit: 1.2.5
subdir: vlc-android
gradle: VanillaX86
gradle:
- VanillaX86
srclibs: VLC@50accb8
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -678,7 +705,8 @@ Builds:
versionCode: 1030003
commit: 1.2.6
subdir: vlc-android
gradle: VanillaARMv6fpu
gradle:
- VanillaARMv6fpu
srclibs: VLC@d59b81a
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -689,7 +717,8 @@ Builds:
versionCode: 1030004
commit: 1.2.6
subdir: vlc-android
gradle: VanillaARMv7
gradle:
- VanillaARMv7
srclibs: VLC@d59b81a
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc
@ -700,7 +729,8 @@ Builds:
versionCode: 1030005
commit: 1.2.6
subdir: vlc-android
gradle: VanillaX86
gradle:
- VanillaX86
srclibs: VLC@d59b81a
prebuild: sed -i -e '/^TARGET/aexit 0' -e 's@\-d \"gradle\/wrapper\"@1@g' ../compile.sh &&
ln -s vlc-android/$$VLC$$ ../vlc

View File

@ -70,7 +70,10 @@ class NightlyTest(unittest.TestCase):
def tearDown(self):
self.tempdir.cleanup()
os.rmdir(self.testroot)
try:
os.rmdir(self.testroot)
except OSError: # other test modules might have left stuff around
pass
def _copy_test_debug_keystore(self):
self.dot_android.mkdir()

53
tests/schemas.TestCase Executable file
View File

@ -0,0 +1,53 @@
#!/usr/bin/env python3
import json
import unittest
import sys # Boilerplate dependency
import importlib
from optparse import OptionParser # Boilerplate dependency
from pathlib import Path # Boilerplate dependency
try:
import jsonschema
except ImportError:
pass
# Boilerplate code
localmodule = Path(__file__).resolve().parent.parent
print('localmodule: ' + str(localmodule))
if localmodule not in sys.path:
sys.path.insert(0, str(localmodule))
import fdroidserver.schemas
class SchemasTest(unittest.TestCase):
@unittest.skipUnless(
importlib.util.find_spec('jsonschema'),
'jsonschema might not be installed, e.g. on Buildserver',
)
def test_schema_is_valid(self):
schema = json.loads(
importlib.resources.files(fdroidserver.schemas)
.joinpath('metadata.json')
.read_text(encoding='utf-8')
)
jsonschema.validators.validator_for(schema).check_schema(schema)
# Boilerplate code
if __name__ == "__main__":
parser = OptionParser()
parser.add_option(
"-v",
"--verbose",
action="store_true",
default=False,
help="Spew out even more information than normal",
)
(fdroidserver.common.options, args) = parser.parse_args(['--verbose'])
newSuite = unittest.TestSuite()
newSuite.addTest(unittest.makeSuite(SchemasTest))
unittest.main(failfast=True)

View File

@ -18,6 +18,9 @@
import os
import sys
import tempfile
import unittest
from pathlib import Path
class TmpCwd:
@ -60,3 +63,12 @@ def mkdtemp():
return tempfile.TemporaryDirectory()
else:
return tempfile.TemporaryDirectory(ignore_cleanup_errors=True)
def mkdir_testfiles(localmodule, test):
"""Keep the test files in a labeled test dir for easy reference"""
testroot = Path(localmodule) / '.testfiles'
testroot.mkdir(exist_ok=True)
testdir = testroot / unittest.TestCase.id(test)
testdir.mkdir(exist_ok=True)
return tempfile.mkdtemp(dir=testdir)