mirror of
https://github.com/f-droid/fdroidserver.git
synced 2025-11-04 22:40:29 +03:00
metadata: get_single_build() for new subcommands
The new subcommands operate on a single APPID:VERSIONCODE pair, so this new function fetches the metadata needed for that operation, and includes any required checks. The algorithm came from: uniqx/fdroidserver@2b779e6599 This reworking of it includes: * removed `check_disabled`, seems like the scheduler should handle that? If it is required, it can be added later. * standard variable names * fixed spelling errors and hopefully improved docstrings * black format * added tests * standardized the strings, made them localizable
This commit is contained in:
parent
e6df386b13
commit
b00b9c9390
3 changed files with 107 additions and 5 deletions
|
|
@ -1236,6 +1236,10 @@ def get_src_tarball_name(appid, versionCode):
|
||||||
return f"{appid}_{versionCode}_src.tar.gz"
|
return f"{appid}_{versionCode}_src.tar.gz"
|
||||||
|
|
||||||
|
|
||||||
|
def get_metadatapath(appid):
|
||||||
|
return f'metadata/{appid}.yml'
|
||||||
|
|
||||||
|
|
||||||
def get_source_date_epoch(build_dir):
|
def get_source_date_epoch(build_dir):
|
||||||
"""Return timestamp suitable for the SOURCE_DATE_EPOCH variable.
|
"""Return timestamp suitable for the SOURCE_DATE_EPOCH variable.
|
||||||
|
|
||||||
|
|
@ -1249,10 +1253,10 @@ def get_source_date_epoch(build_dir):
|
||||||
build_dir = Path(build_dir)
|
build_dir = Path(build_dir)
|
||||||
appid = build_dir.name
|
appid = build_dir.name
|
||||||
data_dir = build_dir.parent.parent
|
data_dir = build_dir.parent.parent
|
||||||
metadata_file = f'metadata/{appid}.yml'
|
metadatapath = get_metadatapath(appid)
|
||||||
if (data_dir / '.git').exists() and (data_dir / metadata_file).exists():
|
if (data_dir / '.git').exists() and (data_dir / metadatapath).exists():
|
||||||
repo = git.repo.Repo(data_dir)
|
repo = git.repo.Repo(data_dir)
|
||||||
return repo.git.log('-n1', '--pretty=%ct', '--', metadata_file)
|
return repo.git.log('-n1', '--pretty=%ct', '--', metadatapath)
|
||||||
|
|
||||||
|
|
||||||
def get_build_dir(app):
|
def get_build_dir(app):
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
# metadata.py - part of the FDroid server tools
|
# metadata.py - part of the FDroid server tools
|
||||||
# Copyright (C) 2013, Ciaran Gultnieks, ciaran@ciarang.com
|
# Copyright (C) 2013, Ciaran Gultnieks, ciaran@ciarang.com
|
||||||
# Copyright (C) 2013-2014 Daniel Martí <mvdan@mvdan.cc>
|
# Copyright (C) 2013-2014 Daniel Martí <mvdan@mvdan.cc>
|
||||||
# Copyright (C) 2017-2018 Michael Pöhn <michael.poehn@fsfe.org>
|
# Copyright (C) 2014-2025 Hans-Christoph Steiner <hans@eds.org>
|
||||||
|
# Copyright (C) 2017-2025 Michael Pöhn <michael.poehn@fsfe.org>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
|
@ -599,6 +600,54 @@ def read_metadata(appid_to_vercode={}, sort_by_time=False):
|
||||||
return apps
|
return apps
|
||||||
|
|
||||||
|
|
||||||
|
def get_single_build(appid, versionCode):
|
||||||
|
"""Read 1 single metadata file from the file system + 1 singled out build.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
appid
|
||||||
|
Application ID app to be loaded (e.g. 'org.fdroid.fdroid')
|
||||||
|
versionCode
|
||||||
|
Android versionCode of the build to be returned
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Tuple
|
||||||
|
A tuple of (app, build) dictionaries, containing the paired data from
|
||||||
|
the metadata file.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
MetaDataException
|
||||||
|
If parsing or selecting the requested version fails.
|
||||||
|
"""
|
||||||
|
metadatapath = common.get_metadatapath(appid)
|
||||||
|
apps = read_metadata({appid: versionCode})
|
||||||
|
if len(apps) != 1 or appid not in apps:
|
||||||
|
raise MetaDataException(
|
||||||
|
_("Could not read {path} for {appid_versionCode}").format(
|
||||||
|
path=metadatapath, appid_versionCode=f"{appid}:{versionCode}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
builds = apps[appid].get("Builds")
|
||||||
|
if not builds:
|
||||||
|
raise MetaDataException(
|
||||||
|
_("{path} has no '{fieldname}'").format(
|
||||||
|
path=metadatapath, fieldname='Builds:'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for build in builds:
|
||||||
|
if build.get('versionCode') == versionCode:
|
||||||
|
return apps[appid], build
|
||||||
|
raise MetaDataException(
|
||||||
|
_("Did not find '{versionCode}' in '{fieldname}' of {path}").format(
|
||||||
|
versionCode=f"versionCode: {versionCode}",
|
||||||
|
fieldname='Builds:',
|
||||||
|
path=metadatapath,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_metadata(metadatapath):
|
def parse_metadata(metadatapath):
|
||||||
"""Parse metadata file, also checking the source repo for .fdroid.yml.
|
"""Parse metadata file, also checking the source repo for .fdroid.yml.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import fdroidserver
|
||||||
from fdroidserver import metadata
|
from fdroidserver import metadata
|
||||||
from fdroidserver._yaml import yaml
|
from fdroidserver._yaml import yaml
|
||||||
from fdroidserver.common import DEFAULT_LOCALE
|
from fdroidserver.common import DEFAULT_LOCALE
|
||||||
from fdroidserver.exception import MetaDataException
|
from fdroidserver.exception import FDroidException, MetaDataException
|
||||||
|
|
||||||
from .shared_test_code import TmpCwd, mkdtemp
|
from .shared_test_code import TmpCwd, mkdtemp
|
||||||
|
|
||||||
|
|
@ -233,6 +233,55 @@ class MetadataTest(unittest.TestCase):
|
||||||
logging_error.assert_called()
|
logging_error.assert_called()
|
||||||
self.assertEqual(3, len(logging_error.call_args_list))
|
self.assertEqual(3, len(logging_error.call_args_list))
|
||||||
|
|
||||||
|
def test_get_single_build(self):
|
||||||
|
"""Test if this can successfully return an app and a build"""
|
||||||
|
os.chdir(self.testdir)
|
||||||
|
appid = 'one.build'
|
||||||
|
versionCode = 1234567890
|
||||||
|
metadatapath = Path(fdroidserver.common.get_metadatapath(appid))
|
||||||
|
metadatapath.parent.mkdir()
|
||||||
|
metadatapath.write_text(f'Name: One\nBuilds:\n - versionCode: {versionCode}\n')
|
||||||
|
app, build = metadata.get_single_build(appid, versionCode)
|
||||||
|
self.assertEqual(app.id, appid)
|
||||||
|
self.assertEqual(build.versionCode, versionCode)
|
||||||
|
|
||||||
|
def test_get_single_build_no_metadatapath(self):
|
||||||
|
"""Test if the right error is thrown if no matching metadatapath found."""
|
||||||
|
os.chdir(self.testdir)
|
||||||
|
appid = 'does.not.exist'
|
||||||
|
versionCode = 1234567890
|
||||||
|
with self.assertLogs(level='DEBUG') as logs:
|
||||||
|
with self.assertRaises(FDroidException):
|
||||||
|
metadata.get_single_build(appid, versionCode)
|
||||||
|
self.assertIn(appid, logs.output[0])
|
||||||
|
self.assertNotIn(str(versionCode), logs.output[0])
|
||||||
|
|
||||||
|
def test_get_single_build_no_builds(self):
|
||||||
|
"""Test if the right error is thrown if Builds: field found."""
|
||||||
|
os.chdir(self.testdir)
|
||||||
|
appid = 'no.builds'
|
||||||
|
versionCode = 1234567890
|
||||||
|
metadatapath = Path(fdroidserver.common.get_metadatapath(appid))
|
||||||
|
metadatapath.parent.mkdir()
|
||||||
|
metadatapath.write_text('Name: No Builds\n')
|
||||||
|
with self.assertRaises(MetaDataException) as e:
|
||||||
|
metadata.get_single_build(appid, versionCode)
|
||||||
|
self.assertIn(appid, e.exception.value)
|
||||||
|
self.assertNotIn(str(versionCode), e.exception.value)
|
||||||
|
|
||||||
|
def test_get_single_build_no_match(self):
|
||||||
|
"""Test if the right error is thrown if Builds: field found."""
|
||||||
|
os.chdir(self.testdir)
|
||||||
|
appid = 'no.builds'
|
||||||
|
versionCode = 1234567890
|
||||||
|
metadatapath = Path(fdroidserver.common.get_metadatapath(appid))
|
||||||
|
metadatapath.parent.mkdir()
|
||||||
|
metadatapath.write_text('Name: No Builds\nBuilds: [{versionCode: 0}]\n')
|
||||||
|
with self.assertRaises(MetaDataException) as e:
|
||||||
|
metadata.get_single_build(appid, versionCode)
|
||||||
|
self.assertIn(appid, e.exception.value)
|
||||||
|
self.assertIn(str(versionCode), e.exception.value)
|
||||||
|
|
||||||
@mock.patch('git.Repo', mock.Mock())
|
@mock.patch('git.Repo', mock.Mock())
|
||||||
def test_metadata_overrides_dot_fdroid_yml(self):
|
def test_metadata_overrides_dot_fdroid_yml(self):
|
||||||
"""Fields in metadata files should override anything in .fdroid.yml."""
|
"""Fields in metadata files should override anything in .fdroid.yml."""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue