From 642499ec944acb11d62f053e5747e6a151eb7872 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 24 Feb 2025 12:48:51 +0100 Subject: [PATCH 1/3] purge config.py handling, it is no longer supported --- .gitignore | 1 - .gitlab-ci.yml | 4 +- MANIFEST.in | 2 +- fdroidserver/common.py | 49 +++------- fdroidserver/init.py | 2 +- tests/config.py | 31 ------- tests/config.yml | 27 ++++++ tests/test_common.py | 198 +++++++++++++---------------------------- tests/test_index.py | 9 +- tests/test_nightly.py | 2 - tests/test_publish.py | 17 ++-- 11 files changed, 117 insertions(+), 225 deletions(-) delete mode 100644 tests/config.py create mode 100644 tests/config.yml diff --git a/.gitignore b/.gitignore index 04e92ad6..ce3a0e9a 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,6 @@ tmp/ /tests/repo/status # files used in manual testing -/config.py /config.yml /tmp/ /logs/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 394c41a4..69b10ae6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -555,11 +555,11 @@ servergitmirrors: - ./tests/key-tricks.py - ssh-keyscan gitlab.com >> /root/.ssh/known_hosts - test -d /tmp/fdroid/repo || mkdir -p /tmp/fdroid/repo - - cp tests/config.py tests/keystore.jks /tmp/fdroid/ + - cp tests/config.yml tests/keystore.jks /tmp/fdroid/ - cp tests/repo/com.politedroid_6.apk /tmp/fdroid/repo/ - cd /tmp/fdroid - touch fdroid-icon.png - - printf "\nservergitmirrors = 'git@gitlab.com:fdroid/ci-test-servergitmirrors-repo.git'\n" >> config.py + - printf "\nservergitmirrors\x3a 'git@gitlab.com:fdroid/ci-test-servergitmirrors-repo.git'\n" >> config.yml - $PYTHONPATH/fdroid update --verbose --create-metadata - $PYTHONPATH/fdroid deploy --verbose - export DLURL=`grep -Eo 'https://gitlab.com/fdroid/ci-test-servergitmirrors-repo[^"]+' repo/index-v1.json` diff --git a/MANIFEST.in b/MANIFEST.in index 4f14a48d..0dac052c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -548,7 +548,7 @@ include tests/build-tools/28.0.3/aapt-output-souch.smsbypass_9.txt include tests/build-tools/generate.sh include tests/check-fdroid-apk include tests/com.fake.IpaApp_1000000000001.ipa -include tests/config.py +include tests/config.yml include tests/config/antiFeatures.yml include tests/config/categories.yml include tests/config/de/antiFeatures.yml diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 21944364..893b14da 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -531,8 +531,7 @@ def read_config(): not required, just use defaults. config.yml is the preferred form because no code is executed when - reading it. config.py is deprecated and supported for backwards - compatibility. + reading it. config.py is deprecated and no longer supported. config.yml requires ASCII or UTF-8 encoding because this code does not auto-detect the file's encoding. That is left up to the YAML @@ -550,10 +549,6 @@ def read_config(): config_file = 'config.yml' old_config_file = 'config.py' - if os.path.exists(config_file) and os.path.exists(old_config_file): - logging.error(_("""Conflicting config files! Using {newfile}, ignoring {oldfile}!""") - .format(oldfile=old_config_file, newfile=config_file)) - if os.path.exists(config_file): logging.debug(_("Reading '{config_file}'").format(config_file=config_file)) with open(config_file, encoding='utf-8') as fp: @@ -561,19 +556,13 @@ def read_config(): if not config: config = {} config_type_check(config_file, config) - elif os.path.exists(old_config_file): - logging.warning(_("""{oldfile} is deprecated, use {newfile}""") - .format(oldfile=old_config_file, newfile=config_file)) - with io.open(old_config_file, "rb") as fp: - code = compile(fp.read(), old_config_file, 'exec') - exec(code, None, config) # nosec TODO automatically migrate - for k in ('mirrors', 'install_list', 'uninstall_list', 'serverwebroot', 'servergitroot'): - if k in config: - if not type(config[k]) in (str, list, tuple): - logging.warning( - _("'{field}' will be in random order! Use () or [] brackets if order is important!") - .format(field=k)) + if os.path.exists(old_config_file): + logging.warning( + _("""Ignoring deprecated {oldfile}, use {newfile}!""").format( + oldfile=old_config_file, newfile=config_file + ) + ) # smartcardoptions must be a list since its command line args for Popen smartcardoptions = config.get('smartcardoptions') @@ -4203,7 +4192,7 @@ def load_stats_fdroid_signing_key_fingerprints(): def write_to_config(thisconfig, key, value=None, config_file=None): - """Write a key/value to the local config.yml or config.py. + """Write a key/value to the local config.yml. NOTE: only supports writing string variables. @@ -4222,8 +4211,6 @@ def write_to_config(thisconfig, key, value=None, config_file=None): value = thisconfig[origkey] if origkey in thisconfig else thisconfig[key] if config_file: cfg = config_file - elif os.path.exists('config.py') and not os.path.exists('config.yml'): - cfg = 'config.py' else: cfg = 'config.yml' @@ -4239,19 +4226,8 @@ def write_to_config(thisconfig, key, value=None, config_file=None): if not lines[-1].endswith('\n'): lines[-1] += '\n' - # regex for finding and replacing python string variable - # definitions/initializations - if cfg.endswith('.py'): - pattern = re.compile(r'^[\s#]*' + key + r'\s*=\s*"[^"]*"') - repl = key + ' = "' + value + '"' - pattern2 = re.compile(r'^[\s#]*' + key + r"\s*=\s*'[^']*'") - repl2 = key + " = '" + value + "'" - else: - # assume .yml as default - pattern = re.compile(r'^[\s#]*' + key + r':.*') - repl = yaml.dump({key: value}, default_flow_style=False) - pattern2 = pattern - repl2 = repl + pattern = re.compile(r'^[\s#]*' + key + r':.*\n') + repl = yaml.dump({key: value}) # If we replaced this line once, we make sure won't be a # second instance of this line for this key in the document. @@ -4259,18 +4235,15 @@ def write_to_config(thisconfig, key, value=None, config_file=None): # edit config file with open(cfg, 'w') as f: for line in lines: - if pattern.match(line) or pattern2.match(line): + if pattern.match(line): if not didRepl: line = pattern.sub(repl, line) - line = pattern2.sub(repl2, line) f.write(line) didRepl = True else: f.write(line) if not didRepl: - f.write('\n') f.write(repl) - f.write('\n') def parse_xml(path): diff --git a/fdroidserver/init.py b/fdroidserver/init.py index b77fea72..326ecf4c 100644 --- a/fdroidserver/init.py +++ b/fdroidserver/init.py @@ -138,7 +138,7 @@ def main(): _("Android SDK not found at {path}!").format(path=test_config['sdk_path']) ) - if not os.path.exists('config.yml') and not os.path.exists('config.py'): + if not os.path.exists('config.yml'): # 'metadata' and 'tmp' are created in fdroid if not os.path.exists('repo'): os.mkdir('repo') diff --git a/tests/config.py b/tests/config.py deleted file mode 100644 index fa118db2..00000000 --- a/tests/config.py +++ /dev/null @@ -1,31 +0,0 @@ - -# TODO convert to config.yml! - -repo_url = "https://MyFirstFDroidRepo.org/fdroid/repo" -repo_name = "My First F-Droid Repo Demo" -repo_description = """This is a repository of apps to be used with F-Droid. Applications in this repository are either official binaries built by the original application developers, or are binaries built from source by the admin of f-droid.org using the tools on https://gitlab.com/fdroid.""" - -archive_older = 3 -archive_url = "https://f-droid.org/archive" -archive_name = "My First F-Droid Archive Demo" -archive_description = """ -The repository of older versions of applications from the main demo repository. -""" - -make_current_version_link = False - -repo_keyalias = "sova" -keystore = "keystore.jks" -keystorepass = "r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI=" -keypass = "r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI=" -keydname = "CN=sova, OU=F-Droid" - -mirrors = ( - 'http://foobarfoobarfoobar.onion/fdroid', - 'https://foo.bar/fdroid', -) - -install_list = 'org.adaway' -uninstall_list = ('com.android.vending', 'com.facebook.orca', ) - -repo_key_sha256 = "f49af3f11efddf20dffd70f5e3117b9976674167adca280e6b1932a0601b26f6" diff --git a/tests/config.yml b/tests/config.yml new file mode 100644 index 00000000..b6f62a44 --- /dev/null +++ b/tests/config.yml @@ -0,0 +1,27 @@ +--- + +repo_url: https://MyFirstFDroidRepo.org/fdroid/repo +repo_name: My First F-Droid Repo Demo +repo_description: This is a repository of apps to be used with F-Droid. Applications in this repository are either official binaries built by the original application developers, or are binaries built from source by the admin of f-droid.org using the tools on https://gitlab.com/fdroid. + +archive_older: 3 +archive_url: https://f-droid.org/archive +archive_name: My First F-Droid Archive Demo +archive_description: The repository of older versions of applications from the main demo repository. + +make_current_version_link: false + +repo_keyalias: sova +keystore: keystore.jks +keystorepass: "r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI=" +keypass: "r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI=" +keydname: "CN=sova, OU=F-Droid" + +mirrors: + - http://foobarfoobarfoobar.onion/fdroid + - https://foo.bar/fdroid + +install_list: org.adaway +uninstall_list: ['com.android.vending', 'com.facebook.orca'] + +repo_key_sha256: f49af3f11efddf20dffd70f5e3117b9976674167adca280e6b1932a0601b26f6 diff --git a/tests/test_common.py b/tests/test_common.py index 79b676aa..34f83773 100755 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -58,6 +58,9 @@ class CommonTest(unittest.TestCase): os.makedirs(self.tmpdir) os.chdir(basedir) + self.verbose = '-v' in sys.argv or '--verbose' in sys.argv + fdroidserver.common.set_console_logging(self.verbose) + # these are declared as None at the top of the module file fdroidserver.common.config = None fdroidserver.common.options = None @@ -642,65 +645,65 @@ class CommonTest(unittest.TestCase): ) def test_write_to_config(self): - with tempfile.TemporaryDirectory() as tmpPath: - cfgPath = os.path.join(tmpPath, 'config.py') - with open(cfgPath, 'w') as f: - f.write( - textwrap.dedent( - """\ + """Test that config items can be added without messing up config.yml. + + The '_orig' key are where the original string values of paths + are stored. Paths have tilde expansion and env vars replaced + in fill_config_defaults(). + + """ + os.chdir(self.testdir) + config_yml = 'config.yml' + Path(config_yml).write_text( + textwrap.dedent( + """\ + # abc + # test: 'example value' + a_path: ~/android-sdk + + # comment + do_not_touch: good value + a_path: "!!!" + + key: "123" # inline""" + ) + ) + + config = {'key': 111, 'a_path_orig': '~/android-sdk'} + fdroidserver.common.write_to_config(config, 'key', config_file=config_yml) + fdroidserver.common.write_to_config( + config, 'a_path', config_file=config_yml + ) + fdroidserver.common.write_to_config( + config, 'test', value='test value', config_file=config_yml + ) + fdroidserver.common.write_to_config( + config, 'new_key', value='new', config_file=config_yml + ) + + with open(config_yml) as fp: + self.assertEqual( + fp.read(), + textwrap.dedent( + """\ # abc - # test = 'example value' - default_me= '%%%' + test: test value + a_path: ~/android-sdk # comment - do_not_touch = "good value" - default_me="!!!" + do_not_touch: good value - key="123" # inline""" - ) - ) - - cfg = {'key': '111', 'default_me_orig': 'orig'} - fdroidserver.common.write_to_config(cfg, 'key', config_file=cfgPath) - fdroidserver.common.write_to_config(cfg, 'default_me', config_file=cfgPath) - fdroidserver.common.write_to_config(cfg, 'test', value='test value', config_file=cfgPath) - fdroidserver.common.write_to_config(cfg, 'new_key', value='new', config_file=cfgPath) - - with open(cfgPath, 'r') as f: - self.assertEqual( - f.read(), - textwrap.dedent( - """\ - # abc - test = 'test value' - default_me = 'orig' - - # comment - do_not_touch = "good value" - - key = "111" # inline - - new_key = "new" + key: 111 + new_key: new """ - ), - ) + ), + ) def test_write_to_config_when_empty(self): - with tempfile.TemporaryDirectory() as tmpPath: - cfgPath = os.path.join(tmpPath, 'config.py') - with open(cfgPath, 'w') as f: - pass - fdroidserver.common.write_to_config({}, 'key', 'val', cfgPath) - with open(cfgPath, 'r') as f: - self.assertEqual( - f.read(), - textwrap.dedent( - """\ - - key = "val" - """ - ), - ) + config_yml = Path(self.testdir) / 'config.yml' + config_yml.write_text('') + fdroidserver.common.write_to_config({}, 'key', 'val', config_yml) + self.assertEqual(config_yml.read_text(), 'key: val\n') def test_apk_name_regex(self): good = [ @@ -1883,7 +1886,6 @@ class CommonTest(unittest.TestCase): """It should set defaults if no config file is found""" os.chdir(self.tmpdir) self.assertFalse(os.path.exists('config.yml')) - self.assertFalse(os.path.exists('config.py')) config = fdroidserver.common.read_config() self.assertIsNotNone(config.get('char_limits')) @@ -1892,7 +1894,6 @@ class CommonTest(unittest.TestCase): os.chdir(self.tmpdir) open('config.yml', 'w').close() self.assertTrue(os.path.exists('config.yml')) - self.assertFalse(os.path.exists('config.py')) config = fdroidserver.common.read_config() self.assertIsNotNone(config.get('char_limits')) @@ -1902,7 +1903,6 @@ class CommonTest(unittest.TestCase): with open('config.yml', 'w') as fp: fp.write('apksigner: yml') self.assertTrue(os.path.exists('config.yml')) - self.assertFalse(os.path.exists('config.py')) config = fdroidserver.common.read_config() self.assertEqual('yml', config.get('apksigner')) @@ -1913,7 +1913,6 @@ class CommonTest(unittest.TestCase): with open('config.yml', 'w', encoding='utf-8') as fp: fp.write('apksigner: ' + teststr) self.assertTrue(os.path.exists('config.yml')) - self.assertFalse(os.path.exists('config.py')) config = fdroidserver.common.read_config() self.assertEqual(teststr, config.get('apksigner')) @@ -1924,7 +1923,6 @@ class CommonTest(unittest.TestCase): with open('config.yml', 'w') as fp: yaml.dump({'apksigner': teststr}, fp) self.assertTrue(os.path.exists('config.yml')) - self.assertFalse(os.path.exists('config.py')) config = fdroidserver.common.read_config() self.assertEqual(teststr, config.get('apksigner')) @@ -1936,7 +1934,6 @@ class CommonTest(unittest.TestCase): with open('config.yml', 'w') as fp: fp.write("""keypass: {'env': 'SECRET'}""") self.assertTrue(os.path.exists('config.yml')) - self.assertFalse(os.path.exists('config.py')) config = fdroidserver.common.read_config() self.assertEqual(os.getenv('SECRET', 'fail'), config.get('keypass')) @@ -1952,16 +1949,6 @@ class CommonTest(unittest.TestCase): with self.assertRaises(yaml.scanner.ScannerError): fdroidserver.common.read_config() - def test_with_config_py(self): - """Make sure it is still possible to use config.py alone.""" - os.chdir(self.tmpdir) - with open('config.py', 'w') as fp: - fp.write('apksigner = "py"') - self.assertFalse(os.path.exists('config.yml')) - self.assertTrue(os.path.exists('config.py')) - config = fdroidserver.common.read_config() - self.assertEqual("py", config.get('apksigner')) - def test_config_perm_warning(self): """Exercise the code path that issues a warning about unsafe permissions.""" os.chdir(self.tmpdir) @@ -1973,24 +1960,6 @@ class CommonTest(unittest.TestCase): os.remove(fp.name) fdroidserver.common.config = None - with open('config.py', 'w') as fp: - fp.write('keystore = "foo.jks"') - self.assertTrue(os.path.exists(fp.name)) - os.chmod(fp.name, 0o666) # nosec B103 - fdroidserver.common.read_config() - - def test_with_both_config_yml_py(self): - """If config.yml and config.py are present, config.py should be ignored.""" - os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write('apksigner: yml') - with open('config.py', 'w') as fp: - fp.write('apksigner = "py"') - self.assertTrue(os.path.exists('config.yml')) - self.assertTrue(os.path.exists('config.py')) - config = fdroidserver.common.read_config() - self.assertEqual('yml', config.get('apksigner')) - def test_config_repo_url(self): """repo_url ends in /repo, archive_url ends in /archive.""" os.chdir(self.tmpdir) @@ -2037,28 +2006,12 @@ class CommonTest(unittest.TestCase): os.chdir(self.tmpdir) with open('config.yml', 'w') as fp: fp.write('apksigner: yml') + os.chmod('config.yml', 0o0600) self.assertTrue(os.path.exists(fp.name)) - self.assertFalse(os.path.exists('config.py')) config = fdroidserver.common.read_config() self.assertFalse('keypass' in config) self.assertEqual('yml', config.get('apksigner')) fdroidserver.common.write_to_config(config, 'keypass', 'mysecretpassword') - with open(fp.name) as fp: - print(fp.read()) - fdroidserver.common.config = None - config = fdroidserver.common.read_config() - self.assertEqual('mysecretpassword', config['keypass']) - - def test_write_to_config_py(self): - os.chdir(self.tmpdir) - with open('config.py', 'w') as fp: - fp.write('apksigner = "py"') - self.assertTrue(os.path.exists(fp.name)) - self.assertFalse(os.path.exists('config.yml')) - config = fdroidserver.common.read_config() - self.assertFalse('keypass' in config) - self.assertEqual('py', config.get('apksigner')) - fdroidserver.common.write_to_config(config, 'keypass', 'mysecretpassword') fdroidserver.common.config = None config = fdroidserver.common.read_config() self.assertEqual('mysecretpassword', config['keypass']) @@ -2068,7 +2021,6 @@ class CommonTest(unittest.TestCase): with open('config.yml', 'w') as fp: fp.write('java_paths:\n 8: /usr/lib/jvm/java-8-openjdk\n') self.assertTrue(os.path.exists(fp.name)) - self.assertFalse(os.path.exists('config.py')) config = fdroidserver.common.read_config() self.assertEqual('/usr/lib/jvm/java-8-openjdk', config.get('java_paths', {}).get('8')) @@ -2143,10 +2095,11 @@ class CommonTest(unittest.TestCase): def test_loading_config_buildserver_yml(self): """Smoke check to make sure this file is properly parsed""" - os.chdir(self.tmpdir) - shutil.copy(os.path.join(basedir, '..', 'buildserver', 'config.buildserver.yml'), - 'config.yml') - self.assertFalse(os.path.exists('config.py')) + os.chdir(self.testdir) + shutil.copy( + os.path.join(basedir, '..', 'buildserver', 'config.buildserver.yml'), + 'config.yml', + ) fdroidserver.common.read_config() def test_setup_status_output(self): @@ -2742,35 +2695,6 @@ class CommonTest(unittest.TestCase): config['smartcardoptions'], ) - def test_get_smartcardoptions_config_py(self): - os.chdir(self.tmpdir) - with open('config.py', 'w') as fp: - fp.write( - textwrap.dedent( - """ - smartcardoptions = ''' - \t-storetype\tPKCS11 - \t-providerClass\tsun.security.pkcs11.SunPKCS11 - \t-providerArg\t/etc/pkcs11_java.cfg - - ''' - """ - ) - ) - config = fdroidserver.common.read_config() - fdroidserver.common.config = config - self.assertEqual( - [ - '-storetype', - 'PKCS11', - '-providerClass', - 'sun.security.pkcs11.SunPKCS11', - '-providerArg', - '/etc/pkcs11_java.cfg', - ], - config['smartcardoptions'], - ) - def test_load_localized_config(self): """It should load""" antiFeatures = fdroidserver.common.load_localized_config( diff --git a/tests/test_index.py b/tests/test_index.py index ea08653b..8f1b1e86 100755 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -38,8 +38,8 @@ class IndexTest(unittest.TestCase): cls.index_v1_jar = basedir / 'repo' / 'index-v1.jar' def setUp(self): - (basedir / 'config.py').chmod(0o600) - os.chdir(basedir) # so read_config() can find config.py + (basedir / 'config.yml').chmod(0o600) + os.chdir(basedir) # so read_config() can find config.yml common.config = None common.options = Options @@ -380,8 +380,7 @@ class IndexTest(unittest.TestCase): with zipfile.ZipFile(jarfile, 'w', zipfile.ZIP_DEFLATED) as jar: jar.writestr('publishsigkeys.json', json.dumps(sigkeyfps)) publish.sign_sig_key_fingerprint_list(jarfile) - with open('config.py', 'w'): - pass + Path('config.yml').write_text('') index.v1_sort_packages( i, common.load_stats_fdroid_signing_key_fingerprints() @@ -701,7 +700,7 @@ class IndexTest(unittest.TestCase): ) def test_add_mirrors_to_repodict(self): - """Test based on the contents of tests/config.py""" + """Test based on the contents of tests/config.yml""" repodict = {'address': common.config['repo_url']} index.add_mirrors_to_repodict('repo', repodict) self.assertEqual( diff --git a/tests/test_nightly.py b/tests/test_nightly.py index 09d8fbcf..bc2567e2 100755 --- a/tests/test_nightly.py +++ b/tests/test_nightly.py @@ -250,7 +250,6 @@ class NightlyTest(unittest.TestCase): raise self.assertEqual(called, [['ssh', '-Tvi'], ['fdroid', 'deploy']]) - self.assertFalse(os.path.exists('config.py')) git_url = 'git@github.com:f-droid/test-nightly' mirror_url = index.get_mirror_service_urls({"url": git_url})[0] expected = { @@ -324,7 +323,6 @@ class NightlyTest(unittest.TestCase): raise self.assertEqual(called, [['ssh', '-Tvi'], ['fdroid', 'deploy']]) - self.assertFalse(os.path.exists('config.py')) expected = { 'archive_description': 'Old nightly builds that have been archived.', 'archive_name': 'fdroid/test-nightly archive', diff --git a/tests/test_publish.py b/tests/test_publish.py index 2f8be36a..eaf2f542 100755 --- a/tests/test_publish.py +++ b/tests/test_publish.py @@ -13,10 +13,12 @@ import json import os import pathlib +import ruamel.yaml import shutil import sys import unittest import tempfile +from pathlib import Path from unittest import mock from fdroidserver import publish @@ -94,8 +96,7 @@ class PublishTest(unittest.TestCase): ] os.chdir(self.testdir) - with open('config.py', 'w') as f: - pass + Path('config.yml').write_text('') publish.store_stats_fdroid_signing_key_fingerprints(appids, indent=2) @@ -116,11 +117,13 @@ class PublishTest(unittest.TestCase): } self.assertEqual(expected, common.load_stats_fdroid_signing_key_fingerprints()) - with open('config.py', 'r') as f: - self.assertEqual( - '\nrepo_key_sha256 = "c58460800c7b250a619c30c13b07b7359a43e5af71a4352d86c58ae18c9f6d41"\n', - f.read(), - ) + yaml = ruamel.yaml.YAML(typ='safe') + with open('config.yml') as fp: + config = yaml.load(fp) + self.assertEqual( + 'c58460800c7b250a619c30c13b07b7359a43e5af71a4352d86c58ae18c9f6d41', + config['repo_key_sha256'], + ) def test_store_and_load_fdroid_signing_key_fingerprints_with_missmatch(self): common.config = {} From 1f96a84f9ad2bf8ef4ecb1f44c876c6ed9495407 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 24 Feb 2025 13:05:37 +0100 Subject: [PATCH 2/3] gitlab-ci: add yamllint job --- .gitlab-ci.yml | 29 +++++++++++++++++++ .../info.guardianproject.urzip/.fdroid.yml | 1 - tests/metadata/duplicate.permisssions.yml | 2 +- tests/metadata/info.guardianproject.urzip.yml | 8 ++--- .../metadata/info.zwanenburg.caffeinetile.yml | 2 +- tests/metadata/org.videolan.vlc.yml | 6 ++-- 6 files changed, 38 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 69b10ae6..6ebc12da 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -274,6 +274,35 @@ safety: - python3 -m safety --key "$SAFETY_API_KEY" --stage cicd scan +# TODO tests/*/*/*.yaml are not covered +yamllint: + image: debian:bookworm-slim + rules: + # once only:/changes: are ported to rules:, this could be removed: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + when: never + - if: $CI_PIPELINE_SOURCE == "push" + changes: + - .gitlab-ci.yml + - .safety-policy.yml + - .yamllint + - tests/*.yml + - tests/*/*.yml + - tests/*/*/.*.yml + <<: *apt-template + variables: + LANG: C.UTF-8 + script: + - apt-get install yamllint + - yamllint + .gitlab-ci.yml + .safety-policy.yml + .yamllint + tests/*.yml + tests/*/*.yml + tests/*/*/.*.yml + + # Run all the various linters and static analysis tools. locales: image: debian:bookworm-slim diff --git a/tests/build/info.guardianproject.urzip/.fdroid.yml b/tests/build/info.guardianproject.urzip/.fdroid.yml index 7f2b3c1c..beea89be 100644 --- a/tests/build/info.guardianproject.urzip/.fdroid.yml +++ b/tests/build/info.guardianproject.urzip/.fdroid.yml @@ -1,4 +1,3 @@ - Summary: This should be overridden by metadata/info.guardianproject.urzip.yml Builds: - versionCode: 50 diff --git a/tests/metadata/duplicate.permisssions.yml b/tests/metadata/duplicate.permisssions.yml index 3401c235..1b721fdb 100644 --- a/tests/metadata/duplicate.permisssions.yml +++ b/tests/metadata/duplicate.permisssions.yml @@ -1,4 +1,4 @@ Categories: -- tests + - tests Name: Duplicate Permisssions Summary: 'Test app for all possible ' diff --git a/tests/metadata/info.guardianproject.urzip.yml b/tests/metadata/info.guardianproject.urzip.yml index d35f7887..cf783f89 100644 --- a/tests/metadata/info.guardianproject.urzip.yml +++ b/tests/metadata/info.guardianproject.urzip.yml @@ -3,10 +3,10 @@ AutoUpdateMode: None Bitcoin: 1Fi5xUHiAPRKxHvyUGVFGt9extBe8Srdbk OpenCollective: f-droid-just-testing Categories: -- Development -- GuardianProject -- 1 -- 2.0 + - Development + - GuardianProject + - 1 + - 2.0 CurrentVersionCode: 2147483647 AuthorWebSite: https://guardianproject.info Description: | diff --git a/tests/metadata/info.zwanenburg.caffeinetile.yml b/tests/metadata/info.zwanenburg.caffeinetile.yml index a1c0b284..50321a81 100644 --- a/tests/metadata/info.zwanenburg.caffeinetile.yml +++ b/tests/metadata/info.zwanenburg.caffeinetile.yml @@ -1,4 +1,4 @@ Categories: -- Development + - Development Name: Caffeine Tile Summary: Test app for extracting icons when an XML one is default diff --git a/tests/metadata/org.videolan.vlc.yml b/tests/metadata/org.videolan.vlc.yml index 266f517d..3d40e9e9 100644 --- a/tests/metadata/org.videolan.vlc.yml +++ b/tests/metadata/org.videolan.vlc.yml @@ -317,7 +317,7 @@ Builds: prebuild: sed -i '/ant/d' ../Makefile && ln -s vlc-android/$$VLC-2.2$$ ../vlc build: cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release -#0.9.10 vercodes were off + # 0.9.10 vercodes were off - versionName: 1.0.0 versionCode: 10006 disable: doesn't build @@ -328,7 +328,7 @@ Builds: prebuild: sed -i '/ant/d' ../Makefile && ln -s vlc-android/$$VLC-2.2$$ ../vlc build: cd ../ && ANDROID_ABI=x86 ./compile.sh release -#0.9.10 vercodes were off + # 0.9.10 vercodes were off - versionName: 1.0.0 versionCode: 10007 disable: doesn't build @@ -339,7 +339,7 @@ Builds: prebuild: sed -i '/ant/d' ../Makefile && ln -s vlc-android/$$VLC-2.2$$ ../vlc build: cd ../ && ANDROID_ABI=armeabi ./compile.sh release -#0.9.10 vercodes were off + # 0.9.10 vercodes were off - versionName: 1.0.0 versionCode: 10008 disable: doesn't build From 3cc6c09ffca3137ae88c4e0de3ebdf613607eb9b Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 26 Feb 2025 18:16:06 +0100 Subject: [PATCH 3/3] use common var for 'config.yml', standarize on UTF-8 This makes it easy to track all the places that use config.yml, and hopefully makes things feel cleaner. This also standardizes all places where config.yml is written out to use UTF-8 as the file encoding. This also includes a lot of black code format fixes. --- fdroidserver/common.py | 56 +++++++------ fdroidserver/init.py | 20 ++--- fdroidserver/lint.py | 2 +- fdroidserver/nightly.py | 4 +- tests/test_build.py | 4 +- tests/test_common.py | 162 +++++++++++++++++++------------------- tests/test_index.py | 6 +- tests/test_init.py | 7 +- tests/test_integration.py | 52 ++++++------ tests/test_lint.py | 12 +-- tests/test_nightly.py | 4 +- tests/test_publish.py | 5 +- tests/test_scanner.py | 4 +- tests/test_update.py | 24 +++--- 14 files changed, 189 insertions(+), 173 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 893b14da..bda93d65 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -126,6 +126,7 @@ CONFIG_NAMES = ( RELEASECHANNELS_CONFIG_NAME, ) +CONFIG_FILE = 'config.yml' config = None options = None @@ -525,7 +526,7 @@ def config_type_check(path, data): def read_config(): """Read the repository config. - The config is read from config_file, which is in the current + The config is read from config.yml, which is in the current directory when any of the repo management commands are used. If there is a local metadata file in the git repo, then the config is not required, just use defaults. @@ -546,21 +547,20 @@ def read_config(): return config config = {} - config_file = 'config.yml' - old_config_file = 'config.py' - if os.path.exists(config_file): - logging.debug(_("Reading '{config_file}'").format(config_file=config_file)) - with open(config_file, encoding='utf-8') as fp: + if os.path.exists(CONFIG_FILE): + logging.debug(_("Reading '{config_file}'").format(config_file=CONFIG_FILE)) + with open(CONFIG_FILE, encoding='utf-8') as fp: config = yaml.safe_load(fp) if not config: config = {} - config_type_check(config_file, config) + config_type_check(CONFIG_FILE, config) + old_config_file = 'config.py' if os.path.exists(old_config_file): logging.warning( _("""Ignoring deprecated {oldfile}, use {newfile}!""").format( - oldfile=old_config_file, newfile=config_file + oldfile=old_config_file, newfile=CONFIG_FILE ) ) @@ -577,14 +577,13 @@ def read_config(): '-providerArg', 'opensc-fdroid.cfg'] if any(k in config for k in ["keystore", "keystorepass", "keypass"]): - if os.path.exists(config_file): - f = config_file - elif os.path.exists(old_config_file): - f = old_config_file - st = os.stat(f) + st = os.stat(CONFIG_FILE) if st.st_mode & stat.S_IRWXG or st.st_mode & stat.S_IRWXO: - logging.warning(_("unsafe permissions on '{config_file}' (should be 0600)!") - .format(config_file=f)) + logging.warning( + _("unsafe permissions on '{config_file}' (should be 0600)!").format( + config_file=CONFIG_FILE + ) + ) fill_config_defaults(config) @@ -4191,9 +4190,17 @@ def load_stats_fdroid_signing_key_fingerprints(): return json.loads(str(f.read('publishsigkeys.json'), 'utf-8')) -def write_to_config(thisconfig, key, value=None, config_file=None): +def write_config_file(config): + """Write the provided string to config.yml with the right path and encoding.""" + Path(CONFIG_FILE).write_text(config, encoding='utf-8') + + +def write_to_config(thisconfig, key, value=None): """Write a key/value to the local config.yml. + The config.yml is defined as YAML 1.2 in UTF-8 encoding on all + platforms. + NOTE: only supports writing string variables. Parameters @@ -4205,21 +4212,18 @@ def write_to_config(thisconfig, key, value=None, config_file=None): value optional value to be written, instead of fetched from 'thisconfig' dictionary. + """ if value is None: origkey = key + '_orig' value = thisconfig[origkey] if origkey in thisconfig else thisconfig[key] - if config_file: - cfg = config_file - else: - cfg = 'config.yml' # load config file, create one if it doesn't exist - if not os.path.exists(cfg): - open(cfg, 'a').close() - logging.info("Creating empty " + cfg) - with open(cfg, 'r') as f: - lines = f.readlines() + if not os.path.exists(CONFIG_FILE): + write_config_file('') + logging.info(_("Creating empty {config_file}").format(config_file=CONFIG_FILE)) + with open(CONFIG_FILE) as fp: + lines = fp.readlines() # make sure the file ends with a carraige return if len(lines) > 0: @@ -4233,7 +4237,7 @@ def write_to_config(thisconfig, key, value=None, config_file=None): # second instance of this line for this key in the document. didRepl = False # edit config file - with open(cfg, 'w') as f: + with open(CONFIG_FILE, 'w', encoding='utf-8') as f: for line in lines: if pattern.match(line): if not didRepl: diff --git a/fdroidserver/init.py b/fdroidserver/init.py index 326ecf4c..a5575fea 100644 --- a/fdroidserver/init.py +++ b/fdroidserver/init.py @@ -38,13 +38,13 @@ def disable_in_config(key, value): """Write a key/value to the local config.yml, then comment it out.""" import yaml - with open('config.yml') as f: - data = f.read() + with open(common.CONFIG_FILE) as fp: + data = fp.read() pattern = r'\n[\s#]*' + key + r':.*' repl = '\n#' + yaml.dump({key: value}, default_flow_style=False) data = re.sub(pattern, repl, data) - with open('config.yml', 'w') as f: - f.writelines(data) + with open(common.CONFIG_FILE, 'w') as fp: + fp.writelines(data) def main(): @@ -138,24 +138,24 @@ def main(): _("Android SDK not found at {path}!").format(path=test_config['sdk_path']) ) - if not os.path.exists('config.yml'): + if not os.path.exists(common.CONFIG_FILE): # 'metadata' and 'tmp' are created in fdroid if not os.path.exists('repo'): os.mkdir('repo') - example_config_yml = os.path.join(examplesdir, 'config.yml') + example_config_yml = os.path.join(examplesdir, common.CONFIG_FILE) if os.path.exists(example_config_yml): - shutil.copyfile(example_config_yml, 'config.yml') + shutil.copyfile(example_config_yml, common.CONFIG_FILE) else: from pkg_resources import get_distribution versionstr = get_distribution('fdroidserver').version if not versionstr: versionstr = 'master' - with open('config.yml', 'w') as fp: + with open(common.CONFIG_FILE, 'w') as fp: fp.write('# see https://gitlab.com/fdroid/fdroidserver/blob/') fp.write(versionstr) - fp.write('/examples/config.yml\n') - os.chmod('config.yml', 0o0600) + fp.write(f'/examples/{common.CONFIG_FILE}\n') + os.chmod(common.CONFIG_FILE, 0o0600) # If android_home is None, test_config['sdk_path'] will be used and # "$ANDROID_HOME" may be used if the env var is set up correctly. # If android_home is not None, the path given from the command line diff --git a/fdroidserver/lint.py b/fdroidserver/lint.py index f8589897..64770476 100644 --- a/fdroidserver/lint.py +++ b/fdroidserver/lint.py @@ -976,7 +976,7 @@ def main(): paths = list() for arg in options.appid: if ( - arg == 'config.yml' + arg == common.CONFIG_FILE or Path(arg).parent.name == 'config' or Path(arg).parent.parent.name == 'config' # localized ): diff --git a/fdroidserver/nightly.py b/fdroidserver/nightly.py index 84c2da2e..5b8983e9 100644 --- a/fdroidserver/nightly.py +++ b/fdroidserver/nightly.py @@ -423,9 +423,9 @@ Last updated: {date}'''.format(repo_git_base=repo_git_base, 'keydname': DISTINGUISHED_NAME, 'make_current_version_link': False, } - with open('config.yml', 'w') as fp: + with open(common.CONFIG_FILE, 'w', encoding='utf-8') as fp: yaml.dump(config, fp, default_flow_style=False) - os.chmod('config.yml', 0o600) + os.chmod(common.CONFIG_FILE, 0o600) config = common.read_config() common.assert_config_keystore(config) diff --git a/tests/test_build.py b/tests/test_build.py index 83e33eb2..5290552e 100755 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -593,9 +593,9 @@ class BuildTest(unittest.TestCase): os.chdir(self.testdir) sdk_path = os.path.join(self.testdir, 'android-sdk') self.create_fake_android_home(sdk_path) - with open('config.yml', 'w') as fp: + with open(fdroidserver.common.CONFIG_FILE, 'w') as fp: yaml.dump({'sdk_path': sdk_path, 'keep_when_not_allowed': True}, fp) - os.chmod('config.yml', 0o600) + os.chmod(fdroidserver.common.CONFIG_FILE, 0o600) fdroidserver.build.config = fdroidserver.common.read_config() os.mkdir('metadata') diff --git a/tests/test_common.py b/tests/test_common.py index 34f83773..17690f59 100755 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -653,8 +653,7 @@ class CommonTest(unittest.TestCase): """ os.chdir(self.testdir) - config_yml = 'config.yml' - Path(config_yml).write_text( + fdroidserver.common.write_config_file( textwrap.dedent( """\ # abc @@ -670,18 +669,12 @@ class CommonTest(unittest.TestCase): ) config = {'key': 111, 'a_path_orig': '~/android-sdk'} - fdroidserver.common.write_to_config(config, 'key', config_file=config_yml) - fdroidserver.common.write_to_config( - config, 'a_path', config_file=config_yml - ) - fdroidserver.common.write_to_config( - config, 'test', value='test value', config_file=config_yml - ) - fdroidserver.common.write_to_config( - config, 'new_key', value='new', config_file=config_yml - ) + fdroidserver.common.write_to_config(config, 'key') + fdroidserver.common.write_to_config(config, 'a_path') + fdroidserver.common.write_to_config(config, 'test', value='test value') + fdroidserver.common.write_to_config(config, 'new_key', value='new') - with open(config_yml) as fp: + with open(fdroidserver.common.CONFIG_FILE) as fp: self.assertEqual( fp.read(), textwrap.dedent( @@ -700,9 +693,10 @@ class CommonTest(unittest.TestCase): ) def test_write_to_config_when_empty(self): - config_yml = Path(self.testdir) / 'config.yml' - config_yml.write_text('') - fdroidserver.common.write_to_config({}, 'key', 'val', config_yml) + os.chdir(self.testdir) + config_yml = Path(fdroidserver.common.CONFIG_FILE) + config_yml.write_text('', encoding='utf-8') + fdroidserver.common.write_to_config({}, 'key', 'val') self.assertEqual(config_yml.read_text(), 'key: val\n') def test_apk_name_regex(self): @@ -1885,24 +1879,23 @@ class CommonTest(unittest.TestCase): def test_with_no_config(self): """It should set defaults if no config file is found""" os.chdir(self.tmpdir) - self.assertFalse(os.path.exists('config.yml')) + self.assertFalse(os.path.exists(fdroidserver.common.CONFIG_FILE)) config = fdroidserver.common.read_config() self.assertIsNotNone(config.get('char_limits')) def test_with_zero_size_config(self): """It should set defaults if config file has nothing in it""" os.chdir(self.tmpdir) - open('config.yml', 'w').close() - self.assertTrue(os.path.exists('config.yml')) + fdroidserver.common.write_config_file('') + self.assertTrue(os.path.exists(fdroidserver.common.CONFIG_FILE)) config = fdroidserver.common.read_config() self.assertIsNotNone(config.get('char_limits')) def test_with_config_yml(self): """Make sure it is possible to use config.yml alone.""" os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write('apksigner: yml') - self.assertTrue(os.path.exists('config.yml')) + fdroidserver.common.write_config_file('apksigner: yml') + self.assertTrue(os.path.exists(fdroidserver.common.CONFIG_FILE)) config = fdroidserver.common.read_config() self.assertEqual('yml', config.get('apksigner')) @@ -1910,9 +1903,8 @@ class CommonTest(unittest.TestCase): """Make sure it is possible to use config.yml in UTF-8 encoding.""" os.chdir(self.tmpdir) teststr = '/πÇÇ现代通用字-български-عربي1/ö/yml' - with open('config.yml', 'w', encoding='utf-8') as fp: - fp.write('apksigner: ' + teststr) - self.assertTrue(os.path.exists('config.yml')) + fdroidserver.common.write_config_file('apksigner: ' + teststr) + self.assertTrue(os.path.exists(fdroidserver.common.CONFIG_FILE)) config = fdroidserver.common.read_config() self.assertEqual(teststr, config.get('apksigner')) @@ -1920,9 +1912,9 @@ class CommonTest(unittest.TestCase): """Make sure it is possible to use config.yml Unicode encoded as ASCII.""" os.chdir(self.tmpdir) teststr = '/πÇÇ现代通用字-български-عربي1/ö/yml' - with open('config.yml', 'w') as fp: + with open(fdroidserver.common.CONFIG_FILE, 'w', encoding='utf-8') as fp: yaml.dump({'apksigner': teststr}, fp) - self.assertTrue(os.path.exists('config.yml')) + self.assertTrue(os.path.exists(fdroidserver.common.CONFIG_FILE)) config = fdroidserver.common.read_config() self.assertEqual(teststr, config.get('apksigner')) @@ -1931,83 +1923,85 @@ class CommonTest(unittest.TestCase): os.chdir(self.tmpdir) with mock.patch.dict(os.environ): os.environ['SECRET'] = 'mysecretpassword' # nosec B105 - with open('config.yml', 'w') as fp: - fp.write("""keypass: {'env': 'SECRET'}""") - self.assertTrue(os.path.exists('config.yml')) + fdroidserver.common.write_config_file("""keypass: {'env': 'SECRET'}\n""") + self.assertTrue(os.path.exists(fdroidserver.common.CONFIG_FILE)) config = fdroidserver.common.read_config() self.assertEqual(os.getenv('SECRET', 'fail'), config.get('keypass')) def test_with_config_yml_is_dict(self): os.chdir(self.tmpdir) - Path('config.yml').write_text('apksigner = /placeholder/path') + Path(fdroidserver.common.CONFIG_FILE).write_text('apksigner = /bin/apksigner') with self.assertRaises(TypeError): fdroidserver.common.read_config() def test_with_config_yml_is_not_mixed_type(self): os.chdir(self.tmpdir) - Path('config.yml').write_text('k: v\napksigner = /placeholder/path') + Path(fdroidserver.common.CONFIG_FILE).write_text('k: v\napksigner = /bin/apk') with self.assertRaises(yaml.scanner.ScannerError): fdroidserver.common.read_config() def test_config_perm_warning(self): """Exercise the code path that issues a warning about unsafe permissions.""" os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write('keystore: foo.jks') - self.assertTrue(os.path.exists(fp.name)) - os.chmod(fp.name, 0o666) # nosec B103 + fdroidserver.common.write_config_file('keystore: foo.jks') + self.assertTrue(os.path.exists(fdroidserver.common.CONFIG_FILE)) + os.chmod(fdroidserver.common.CONFIG_FILE, 0o666) # nosec B103 fdroidserver.common.read_config() - os.remove(fp.name) + os.remove(fdroidserver.common.CONFIG_FILE) fdroidserver.common.config = None def test_config_repo_url(self): """repo_url ends in /repo, archive_url ends in /archive.""" os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write('repo_url: https://MyFirstFDroidRepo.org/fdroid/repo\n') - fp.write('archive_url: https://MyFirstFDroidRepo.org/fdroid/archive') + fdroidserver.common.write_config_file( + textwrap.dedent( + """\ + repo_url: https://MyFirstFDroidRepo.org/fdroid/repo + archive_url: https://MyFirstFDroidRepo.org/fdroid/archive + """ + ) + ) config = fdroidserver.common.read_config() - self.assertEqual('https://MyFirstFDroidRepo.org/fdroid/repo', config.get('repo_url')) - self.assertEqual('https://MyFirstFDroidRepo.org/fdroid/archive', config.get('archive_url')) + self.assertEqual( + 'https://MyFirstFDroidRepo.org/fdroid/repo', config.get('repo_url') + ) + self.assertEqual( + 'https://MyFirstFDroidRepo.org/fdroid/archive', config.get('archive_url') + ) def test_config_repo_url_extra_slash(self): """repo_url ends in /repo, archive_url ends in /archive.""" os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write('repo_url: https://MyFirstFDroidRepo.org/fdroid/repo/') + fdroidserver.common.write_config_file('repo_url: https://MyFirstFDroidRepo.org/fdroid/repo/') with self.assertRaises(FDroidException): fdroidserver.common.read_config() def test_config_repo_url_not_repo(self): """repo_url ends in /repo, archive_url ends in /archive.""" os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write('repo_url: https://MyFirstFDroidRepo.org/fdroid/foo') + fdroidserver.common.write_config_file('repo_url: https://MyFirstFDroidRepo.org/fdroid/foo') with self.assertRaises(FDroidException): fdroidserver.common.read_config() def test_config_archive_url_extra_slash(self): """repo_url ends in /repo, archive_url ends in /archive.""" os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write('archive_url: https://MyFirstFDroidRepo.org/fdroid/archive/') + fdroidserver.common.write_config_file('archive_url: https://MyFirstFDroidRepo.org/fdroid/archive/') with self.assertRaises(FDroidException): fdroidserver.common.read_config() def test_config_archive_url_not_repo(self): """repo_url ends in /repo, archive_url ends in /archive.""" os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write('archive_url: https://MyFirstFDroidRepo.org/fdroid/foo') + fdroidserver.common.write_config_file('archive_url: https://MyFirstFDroidRepo.org/fdroid/foo') with self.assertRaises(FDroidException): fdroidserver.common.read_config() def test_write_to_config_yml(self): os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write('apksigner: yml') - os.chmod('config.yml', 0o0600) - self.assertTrue(os.path.exists(fp.name)) + fdroidserver.common.write_config_file('apksigner: yml') + os.chmod(fdroidserver.common.CONFIG_FILE, 0o0600) + self.assertTrue(os.path.exists(fdroidserver.common.CONFIG_FILE)) config = fdroidserver.common.read_config() self.assertFalse('keypass' in config) self.assertEqual('yml', config.get('apksigner')) @@ -2018,11 +2012,17 @@ class CommonTest(unittest.TestCase): def test_config_dict_with_int_keys(self): os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write('java_paths:\n 8: /usr/lib/jvm/java-8-openjdk\n') - self.assertTrue(os.path.exists(fp.name)) + fdroidserver.common.write_config_file( + textwrap.dedent( + """ + java_paths: + 8: /usr/lib/jvm/java-8-openjdk + """ + ) + ) + self.assertTrue(os.path.exists(fdroidserver.common.CONFIG_FILE)) config = fdroidserver.common.read_config() - self.assertEqual('/usr/lib/jvm/java-8-openjdk', config.get('java_paths', {}).get('8')) + self.assertEqual('/usr/lib/jvm/java-8-openjdk', config['java_paths']['8']) @mock.patch.dict(os.environ, {'PATH': os.getenv('PATH')}, clear=True) def test_test_sdk_exists_fails_on_bad_sdk_path(self): @@ -2098,7 +2098,7 @@ class CommonTest(unittest.TestCase): os.chdir(self.testdir) shutil.copy( os.path.join(basedir, '..', 'buildserver', 'config.buildserver.yml'), - 'config.yml', + fdroidserver.common.CONFIG_FILE, ) fdroidserver.common.read_config() @@ -2606,7 +2606,7 @@ class CommonTest(unittest.TestCase): def test_get_apksigner_smartcardoptions(self): os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: + with open(fdroidserver.common.CONFIG_FILE, 'w', encoding='utf-8') as fp: d = { 'smartcardoptions': '-storetype PKCS11' ' -providerName SunPKCS11-OpenSC' @@ -2634,10 +2634,9 @@ class CommonTest(unittest.TestCase): def test_get_smartcardoptions_list(self): os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write( - textwrap.dedent( - """ + fdroidserver.common.write_config_file( + textwrap.dedent( + """ smartcardoptions: - -storetype - PKCS11 @@ -2647,9 +2646,9 @@ class CommonTest(unittest.TestCase): - sun.security.pkcs11.SunPKCS11 - -providerArg - opensc-fdroid.cfg - """ - ) + """ ) + ) config = fdroidserver.common.read_config() fdroidserver.common.config = config self.assertTrue(isinstance(config['smartcardoptions'], list)) @@ -2669,17 +2668,16 @@ class CommonTest(unittest.TestCase): def test_get_smartcardoptions_spaces(self): os.chdir(self.tmpdir) - with open('config.yml', 'w') as fp: - fp.write( - textwrap.dedent( - """smartcardoptions: | - -storetype PKCS11 - -providerClass sun.security.pkcs11.SunPKCS11 - -providerArg /etc/pkcs11_java.cfg - - """ - ) + fdroidserver.common.write_config_file( + textwrap.dedent( + """ + smartcardoptions: | + -storetype PKCS11 + -providerClass sun.security.pkcs11.SunPKCS11 + -providerArg /etc/pkcs11_java.cfg + """ ) + ) config = fdroidserver.common.read_config() fdroidserver.common.config = config self.assertTrue(isinstance(config['smartcardoptions'], list)) @@ -2801,7 +2799,9 @@ class CommonTest(unittest.TestCase): def test_config_serverwebroot_str(self): os.chdir(self.testdir) - Path('config.yml').write_text("""serverwebroot: 'foo@example.com:/var/www'""") + fdroidserver.common.write_config_file( + """serverwebroot: 'foo@example.com:/var/www'""" + ) self.assertEqual( [{'url': 'foo@example.com:/var/www/'}], fdroidserver.common.read_config()['serverwebroot'], @@ -2809,7 +2809,9 @@ class CommonTest(unittest.TestCase): def test_config_serverwebroot_list(self): os.chdir(self.testdir) - Path('config.yml').write_text("""serverwebroot:\n - foo@example.com:/var/www""") + fdroidserver.common.write_config_file( + """serverwebroot:\n - foo@example.com:/var/www""" + ) self.assertEqual( [{'url': 'foo@example.com:/var/www/'}], fdroidserver.common.read_config()['serverwebroot'], @@ -2817,7 +2819,9 @@ class CommonTest(unittest.TestCase): def test_config_serverwebroot_dict(self): os.chdir(self.testdir) - Path('config.yml').write_text("""serverwebroot:\n - url: 'foo@example.com:/var/www'""") + fdroidserver.common.write_config_file( + """serverwebroot:\n - url: 'foo@example.com:/var/www'""" + ) self.assertEqual( [{'url': 'foo@example.com:/var/www/'}], fdroidserver.common.read_config()['serverwebroot'], diff --git a/tests/test_index.py b/tests/test_index.py index 8f1b1e86..059386fb 100755 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -38,7 +38,7 @@ class IndexTest(unittest.TestCase): cls.index_v1_jar = basedir / 'repo' / 'index-v1.jar' def setUp(self): - (basedir / 'config.yml').chmod(0o600) + (basedir / common.CONFIG_FILE).chmod(0o600) os.chdir(basedir) # so read_config() can find config.yml common.config = None @@ -380,7 +380,7 @@ class IndexTest(unittest.TestCase): with zipfile.ZipFile(jarfile, 'w', zipfile.ZIP_DEFLATED) as jar: jar.writestr('publishsigkeys.json', json.dumps(sigkeyfps)) publish.sign_sig_key_fingerprint_list(jarfile) - Path('config.yml').write_text('') + common.write_config_file('') index.v1_sort_packages( i, common.load_stats_fdroid_signing_key_fingerprints() @@ -717,7 +717,7 @@ class IndexTest(unittest.TestCase): os.chdir(self.testdir) repo_url = 'https://example.com/fdroid/repo' c = {'repo_url': repo_url, 'mirrors': ['http://one/fdroid']} - with open('config.yml', 'w') as fp: + with open(common.CONFIG_FILE, 'w', encoding='utf-8') as fp: yaml.dump(c, fp) common.config = None common.read_config() diff --git a/tests/test_init.py b/tests/test_init.py index b448192f..179f06c7 100755 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -28,12 +28,13 @@ class InitTest(unittest.TestCase): self._td.cleanup() def test_disable_in_config(self): - configfile = pathlib.Path('config.yml') - configfile.write_text('keystore: NONE\nkeypass: mysupersecrets\n') + test = 'mysupersecrets' + configfile = pathlib.Path(fdroidserver.common.CONFIG_FILE) + configfile.write_text(f'keystore: NONE\nkeypass: {test}\n', encoding='utf-8') configfile.chmod(0o600) config = fdroidserver.common.read_config() self.assertEqual('NONE', config['keystore']) - self.assertEqual('mysupersecrets', config['keypass']) + self.assertEqual(test, config['keypass']) fdroidserver.init.disable_in_config('keypass', 'comment') self.assertIn('#keypass:', configfile.read_text()) fdroidserver.common.config = None diff --git a/tests/test_integration.py b/tests/test_integration.py index d7f1231d..7c527b57 100755 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -148,7 +148,7 @@ class IntegrationTest(unittest.TestCase): + ["init", "--keystore", keystore_path, "--repo-keyalias", "sova"] ) self.update_yaml( - "config.yml", + common.CONFIG_FILE, { "keystorepass": "r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI=", "keypass": "r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI=", @@ -160,7 +160,7 @@ class IntegrationTest(unittest.TestCase): shutil.copy(FILES / "keystore.jks", "keystore.jks") self.fdroid_init_with_prebuilt_keystore("keystore.jks") self.update_yaml( - "config.yml", + common.CONFIG_FILE, { "make_current_version_link": True, "keydname": "CN=Birdman, OU=Cell, O=Alcatraz, L=Alcatraz, S=California, C=US", @@ -193,7 +193,7 @@ class IntegrationTest(unittest.TestCase): def test_utf8_metadata(self): self.fdroid_init_with_prebuilt_keystore() self.update_yaml( - "config.yml", + common.CONFIG_FILE, { "repo_description": "获取已安装在您的设备上的应用的", "mirrors": ["https://foo.bar/fdroid", "http://secret.onion/fdroid"], @@ -247,7 +247,7 @@ class IntegrationTest(unittest.TestCase): shutil.copytree(FILES / "gnupghome", gnupghome) os.chmod(gnupghome, 0o700) self.update_yaml( - "config.yml", + common.CONFIG_FILE, { "install_list": "org.adaway", "uninstall_list": ["com.android.vending", "com.facebook.orca"], @@ -334,7 +334,7 @@ class IntegrationTest(unittest.TestCase): FILES.glob("repo/obb.main.twoversions_110161[357].apk"), ): shutil.copy(f, "repo") - self.update_yaml("config.yml", {"archive_older": 3}) + self.update_yaml(common.CONFIG_FILE, {"archive_older": 3}) self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) with open("archive/index.xml") as f: @@ -356,7 +356,7 @@ class IntegrationTest(unittest.TestCase): shutil.copy(FILES / "metadata/com.politedroid.yml", "metadata") for f in FILES.glob("repo/com.politedroid_[0-9].apk"): shutil.copy(f, "repo") - self.update_yaml("config.yml", {"archive_older": 3}) + self.update_yaml(common.CONFIG_FILE, {"archive_older": 3}) self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) repo = Path("repo/index.xml").read_text() @@ -456,7 +456,7 @@ class IntegrationTest(unittest.TestCase): self.remove_lines("metadata/com.politedroid.yml", ["ArchivePolicy:"]) for f in FILES.glob("repo/com.politedroid_[0-9].apk"): shutil.copy(f, "repo") - self.update_yaml("config.yml", {"archive_older": 3}) + self.update_yaml(common.CONFIG_FILE, {"archive_older": 3}) self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) repo = Path("repo/index.xml").read_text() @@ -474,7 +474,7 @@ class IntegrationTest(unittest.TestCase): self.assertIn("com.politedroid_3.apk", archive) self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) - self.update_yaml("config.yml", {"archive_older": 1}) + self.update_yaml(common.CONFIG_FILE, {"archive_older": 1}) self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) repo = Path("repo/index.xml").read_text() repo_cnt = sum(1 for line in repo.splitlines() if "" in line) @@ -555,7 +555,7 @@ class IntegrationTest(unittest.TestCase): def test_allowing_disabled_signatures_in_repo_and_archive(self): self.fdroid_init_with_prebuilt_keystore() self.update_yaml( - "config.yml", {"allow_disabled_algorithms": True, "archive_older": 3} + common.CONFIG_FILE, {"allow_disabled_algorithms": True, "archive_older": 3} ) Path("metadata").mkdir() shutil.copy(FILES / "metadata/com.politedroid.yml", "metadata") @@ -650,7 +650,7 @@ class IntegrationTest(unittest.TestCase): self.assertTrue(Path("archive/urzip-badsig.apk").is_file()) # test unarchiving when disabled_algorithms are allowed again - self.update_yaml("config.yml", {"allow_disabled_algorithms": True}) + self.update_yaml(common.CONFIG_FILE, {"allow_disabled_algorithms": True}) self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) with open("archive/index.xml") as f: archive_cnt = sum(1 for line in f if "" in line) @@ -689,7 +689,7 @@ class IntegrationTest(unittest.TestCase): def test_rename_apks_with_fdroid_update_rename_apks_opt_nosign_opt_for_speed(self): self.fdroid_init_with_prebuilt_keystore() self.update_yaml( - "config.yml", + common.CONFIG_FILE, { "keydname": "CN=Birdman, OU=Cell, O=Alcatraz, L=Alcatraz, S=California, C=US" }, @@ -744,7 +744,7 @@ class IntegrationTest(unittest.TestCase): def test_for_added_date_being_set_correctly_for_repo_and_archive(self): self.fdroid_init_with_prebuilt_keystore() - self.update_yaml("config.yml", {"archive_older": 3}) + self.update_yaml(common.CONFIG_FILE, {"archive_older": 3}) Path("metadata").mkdir() Path("archive").mkdir() Path("stats").mkdir() @@ -803,12 +803,14 @@ class IntegrationTest(unittest.TestCase): ) # fake that no JDKs are available self.update_yaml( - "config.yml", {"categories": ["Internet"], "java_paths": {}}, replace=True + common.CONFIG_FILE, + {"categories": ["Internet"], "java_paths": {}}, + replace=True, ) local_copy_dir = self.testdir / "local_copy_dir/fdroid" (local_copy_dir / "repo").mkdir(parents=True) self.update_yaml( - "config.yml", {"local_copy_dir": str(local_copy_dir.resolve())} + common.CONFIG_FILE, {"local_copy_dir": str(local_copy_dir.resolve())} ) subprocess.run(self.fdroid_cmd + ["checkupdates", "--allow-dirty"]) @@ -879,7 +881,7 @@ class IntegrationTest(unittest.TestCase): new_tmp_repo.mkdir() os.chdir(new_tmp_repo) self.fdroid_init_with_prebuilt_keystore() - self.update_yaml("config.yml", {"sync_from_local_copy_dir": True}) + self.update_yaml(common.CONFIG_FILE, {"sync_from_local_copy_dir": True}) self.assert_run( self.fdroid_cmd + ["deploy", "--local-copy-dir", local_copy_dir] ) @@ -946,7 +948,7 @@ class IntegrationTest(unittest.TestCase): ] ) # the value set in --android-home should override $ANDROID_HOME - self.assertIn(str(fake_android_home), Path("config.yml").read_text()) + self.assertIn(str(fake_android_home), Path(common.CONFIG_FILE).read_text()) @unittest.skipUnless( "ANDROID_HOME" in os.environ, "runs only with ANDROID_HOME set" @@ -1097,7 +1099,7 @@ class IntegrationTest(unittest.TestCase): # now set up fake, non-working keystore setup Path("keystore.p12").touch() self.update_yaml( - "config.yml", + common.CONFIG_FILE, { "keystore": "keystore.p12", "repo_keyalias": "foo", @@ -1115,7 +1117,9 @@ class IntegrationTest(unittest.TestCase): shutil.copytree(FILES / "repo", "repo", dirs_exist_ok=True) shutil.copytree(FILES / "metadata", "metadata") git_remote = self.testdir / "git_remote" - self.update_yaml("config.yml", {"binary_transparency_remote": str(git_remote)}) + self.update_yaml( + common.CONFIG_FILE, {"binary_transparency_remote": str(git_remote)} + ) self.assert_run(self.fdroid_cmd + ["update", "--verbose"]) self.assert_run(self.fdroid_cmd + ["deploy", "--verbose"]) self.assertIn(" 0) # now set fake repo_keyalias - self.update_yaml("config.yml", {"repo_keyalias": "fake"}) + self.update_yaml(common.CONFIG_FILE, {"repo_keyalias": "fake"}) # this should fail because this repo has a bad repo_keyalias self.assert_run_fail(self.fdroid_cmd + ["update"]) @@ -1179,7 +1183,7 @@ class IntegrationTest(unittest.TestCase): self.fdroid_init_with_prebuilt_keystore() self.update_yaml( - "config.yml", + common.CONFIG_FILE, {"archive_older": 3, "servergitmirrors": str(server_git_mirror)}, ) for f in FILES.glob("repo/com.politedroid_[345].apk"): @@ -1226,7 +1230,7 @@ class IntegrationTest(unittest.TestCase): f.stat().st_size for f in (git_mirror / ".git").glob("**/*") if f.is_file() ) - self.update_yaml("config.yml", {"git_mirror_size_limit": "60kb"}) + self.update_yaml(common.CONFIG_FILE, {"git_mirror_size_limit": "60kb"}) self.assert_run(self.fdroid_cmd + ["update"]) self.assert_run(self.fdroid_cmd + ["deploy"]) self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) @@ -1274,7 +1278,7 @@ class IntegrationTest(unittest.TestCase): Path("unsigned").mkdir() shutil.copy(FILES / "urzip-release-unsigned.apk", "unsigned") self.update_yaml( - "config.yml", + common.CONFIG_FILE, { "archive_older": 3, "mirrors": [ @@ -1312,7 +1316,7 @@ class IntegrationTest(unittest.TestCase): ) os.chdir(online_root) self.update_yaml( - "config.yml", + common.CONFIG_FILE, { "local_copy_dir": str(local_copy_dir), "sync_from_local_copy_dir": True, @@ -1344,7 +1348,7 @@ class IntegrationTest(unittest.TestCase): def test_extracting_and_publishing_with_developer_signature(self): self.fdroid_init_with_prebuilt_keystore() self.update_yaml( - "config.yml", + common.CONFIG_FILE, { "keydname": "CN=Birdman, OU=Cell, O=Alcatraz, L=Alcatraz, S=California, C=US" }, diff --git a/tests/test_lint.py b/tests/test_lint.py index ff6c2247..6816ab69 100755 --- a/tests/test_lint.py +++ b/tests/test_lint.py @@ -345,7 +345,7 @@ class LintTest(unittest.TestCase): def test_check_categories_from_config_yml(self): """In config.yml, categories is a list.""" os.chdir(self.testdir) - Path('config.yml').write_text('categories: [foo, bar]\n') + fdroidserver.common.write_config_file('categories: [foo, bar]\n') fdroidserver.lint.config = fdroidserver.common.read_config() fdroidserver.lint.load_categories_config() self.assertEqual(fdroidserver.lint.CATEGORIES_KEYS, ['foo', 'bar']) @@ -434,9 +434,11 @@ class LintTest(unittest.TestCase): def test_lint_invalid_config_keys(self): os.chdir(self.testdir) - Path('config').mkdir() - Path('config/config.yml').write_text('repo:\n invalid_key: test\n') - self.assertFalse(fdroidserver.lint.lint_config('config/config.yml')) + os.mkdir('config') + config_yml = fdroidserver.common.CONFIG_FILE + with open(f'config/{config_yml}', 'w', encoding='utf-8') as fp: + fp.write('repo:\n invalid_key: test\n') + self.assertFalse(fdroidserver.lint.lint_config(f'config/{config_yml}')) def test_lint_invalid_localized_config_keys(self): os.chdir(self.testdir) @@ -534,7 +536,7 @@ class LintAntiFeaturesTest(unittest.TestCase): class ConfigYmlTest(LintTest): def setUp(self): super().setUp() - self.config_yml = Path(self.testdir) / 'config.yml' + self.config_yml = Path(self.testdir) / fdroidserver.common.CONFIG_FILE def test_config_yml_int(self): self.config_yml.write_text('repo_maxage: 1\n') diff --git a/tests/test_nightly.py b/tests/test_nightly.py index bc2567e2..750a22fc 100755 --- a/tests/test_nightly.py +++ b/tests/test_nightly.py @@ -268,7 +268,7 @@ class NightlyTest(unittest.TestCase): 'repo_url': mirror_url + '/repo', 'servergitmirrors': [{"url": git_url}], } - with open('config.yml') as fp: + with open(common.CONFIG_FILE) as fp: config = yaml.safe_load(fp) # .ssh is random tmpdir set in nightly.py, so test basename only self.assertEqual( @@ -339,7 +339,7 @@ class NightlyTest(unittest.TestCase): 'repo_url': 'https://gitlab.com/fdroid/test-nightly/-/raw/master/fdroid/repo', 'servergitmirrors': [{"url": 'git@gitlab.com:fdroid/test-nightly'}], } - with open('config.yml') as fp: + with open(common.CONFIG_FILE) as fp: config = yaml.safe_load(fp) # .ssh is random tmpdir set in nightly.py, so test basename only self.assertEqual( diff --git a/tests/test_publish.py b/tests/test_publish.py index eaf2f542..099c4188 100755 --- a/tests/test_publish.py +++ b/tests/test_publish.py @@ -18,7 +18,6 @@ import shutil import sys import unittest import tempfile -from pathlib import Path from unittest import mock from fdroidserver import publish @@ -96,7 +95,7 @@ class PublishTest(unittest.TestCase): ] os.chdir(self.testdir) - Path('config.yml').write_text('') + common.write_config_file('') publish.store_stats_fdroid_signing_key_fingerprints(appids, indent=2) @@ -118,7 +117,7 @@ class PublishTest(unittest.TestCase): self.assertEqual(expected, common.load_stats_fdroid_signing_key_fingerprints()) yaml = ruamel.yaml.YAML(typ='safe') - with open('config.yml') as fp: + with open(common.CONFIG_FILE) as fp: config = yaml.load(fp) self.assertEqual( 'c58460800c7b250a619c30c13b07b7359a43e5af71a4352d86c58ae18c9f6d41', diff --git a/tests/test_scanner.py b/tests/test_scanner.py index 592680ef..4899a219 100755 --- a/tests/test_scanner.py +++ b/tests/test_scanner.py @@ -800,7 +800,7 @@ class Test_ScannerTool(unittest.TestCase): def test_refresh_from_config(self): os.chdir(self.testdir) - pathlib.Path('config.yml').write_text('refresh_scanner: true') + fdroidserver.common.write_config_file('refresh_scanner: true\n') with mock.patch('fdroidserver.scanner.ScannerTool.refresh') as refresh: fdroidserver.scanner.ScannerTool() refresh.assert_called_once() @@ -809,7 +809,7 @@ class Test_ScannerTool(unittest.TestCase): fdroidserver.common.options = mock.Mock() fdroidserver.common.options.refresh_scanner = True os.chdir(self.testdir) - pathlib.Path('config.yml').write_text('refresh_scanner: false') + fdroidserver.common.write_config_file('refresh_scanner: false\n') with mock.patch('fdroidserver.scanner.ScannerTool.refresh') as refresh: fdroidserver.scanner.ScannerTool() refresh.assert_called_once() diff --git a/tests/test_update.py b/tests/test_update.py index 28dd27f9..6c551694 100755 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -1787,7 +1787,9 @@ class UpdateTest(unittest.TestCase): def test_categories_txt_is_removed_by_delete_unknown(self): """categories.txt used to be a part of this system, now its nothing.""" os.chdir(self.testdir) - Path('config.yml').write_text('repo_pubkey: ffffffffffffffffffffffffffffffffffffffff') + fdroidserver.common.write_config_file( + 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff\n' + ) categories_txt = Path('repo/categories.txt') categories_txt.parent.mkdir() @@ -1803,8 +1805,8 @@ class UpdateTest(unittest.TestCase): os.chdir(self.testdir) os.mkdir('metadata') os.mkdir('repo') - Path('config.yml').write_text( - 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff' + fdroidserver.common.write_config_file( + 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff\n' ) testapk = os.path.join('repo', 'com.politedroid_6.apk') @@ -1822,8 +1824,8 @@ class UpdateTest(unittest.TestCase): os.chdir(self.testdir) os.mkdir('metadata') os.mkdir('repo') - Path('config.yml').write_text( - 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff' + fdroidserver.common.write_config_file( + 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff\n' ) testapk = os.path.join('repo', 'com.politedroid_6.apk') @@ -1844,8 +1846,8 @@ class UpdateTest(unittest.TestCase): os.chdir(self.testdir) os.mkdir('metadata') os.mkdir('repo') - Path('config.yml').write_text( - 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff' + fdroidserver.common.write_config_file( + 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff\n' ) testapk = os.path.join('repo', 'com.politedroid_6.apk') @@ -1871,8 +1873,8 @@ class UpdateTest(unittest.TestCase): Path('config/categories.yml').write_text('System: {name: System Apps}') os.mkdir('metadata') os.mkdir('repo') - Path('config.yml').write_text( - 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff' + fdroidserver.common.write_config_file( + 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff\n' ) testapk = os.path.join('repo', 'com.politedroid_6.apk') @@ -1901,8 +1903,8 @@ class UpdateTest(unittest.TestCase): Path('config/categories.yml').write_text('System: {name: S}\nTime: {name: T}\n') os.mkdir('metadata') os.mkdir('repo') - Path('config.yml').write_text( - 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff' + fdroidserver.common.write_config_file( + 'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff\n' ) testapk = os.path.join('repo', 'com.politedroid_6.apk')