mirror of
				https://github.com/f-droid/fdroidserver.git
				synced 2025-11-04 14:30:30 +03:00 
			
		
		
		
	Merge branch 'get_single_build' into 'master'
metadata: get_single_build() for new subcommands See merge request fdroid/fdroidserver!1708
This commit is contained in:
		
						commit
						3e5a8e625d
					
				
					 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