standardize config on ruamel.yaml with a YAML 1.2 config
This is a key piece of the ongoing `PUBLISH` _config.yml_ migration. There was uneven implementation of which YAML parser to use, and that could lead to bugs where one parser might read a value one way, and a different parser will read the value a different way. I wanted to be sure that YAML 1.2 would always work. This makes all code that handles config files use the same `ruamel.yaml` parsers. This only touches other usages of YAML parsers when there is overlap. This does not port all of _fdroidserver_ to `ruamel.yaml` and YAML 1.2. The metadata files should already be YAML 1.2 anyway. # Conflicts: # fdroidserver/lint.py
This commit is contained in:
parent
53b62415d3
commit
2f47938dbf
40
fdroidserver/_yaml.py
Normal file
40
fdroidserver/_yaml.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Copyright (C) 2025, Hans-Christoph Steiner <hans@eds.org>
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""Standard YAML parsing and dumping.
|
||||||
|
|
||||||
|
YAML 1.2 is the preferred format for all data files. When loading
|
||||||
|
F-Droid formats like config.yml and <Application ID>.yml, YAML 1.2 is
|
||||||
|
forced, and older YAML constructs should be considered an error.
|
||||||
|
|
||||||
|
It is OK to load and dump files in other YAML versions if they are
|
||||||
|
externally defined formats, like FUNDING.yml. In those cases, these
|
||||||
|
common instances might not be appropriate to use.
|
||||||
|
|
||||||
|
There is a separate instance for dumping based on the "round trip" aka
|
||||||
|
"rt" mode. The "rt" mode maintains order while the "safe" mode sorts
|
||||||
|
the output. Also, yaml.version is not forced in the dumper because that
|
||||||
|
makes it write out a "%YAML 1.2" header. F-Droid's formats are
|
||||||
|
explicitly defined as YAML 1.2 and meant to be human-editable. So that
|
||||||
|
header gets in the way.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import ruamel.yaml
|
||||||
|
|
||||||
|
yaml = ruamel.yaml.YAML(typ='safe')
|
||||||
|
yaml.version = (1, 2)
|
||||||
|
|
||||||
|
yaml_dumper = ruamel.yaml.YAML(typ='rt')
|
@ -39,6 +39,7 @@ import sys
|
|||||||
import re
|
import re
|
||||||
import ast
|
import ast
|
||||||
import gzip
|
import gzip
|
||||||
|
import ruamel.yaml
|
||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -48,7 +49,6 @@ import logging
|
|||||||
import hashlib
|
import hashlib
|
||||||
import socket
|
import socket
|
||||||
import base64
|
import base64
|
||||||
import yaml
|
|
||||||
import zipfile
|
import zipfile
|
||||||
import tempfile
|
import tempfile
|
||||||
import json
|
import json
|
||||||
@ -67,6 +67,7 @@ from zipfile import ZipFile
|
|||||||
|
|
||||||
import fdroidserver.metadata
|
import fdroidserver.metadata
|
||||||
from fdroidserver import _
|
from fdroidserver import _
|
||||||
|
from fdroidserver._yaml import yaml, yaml_dumper
|
||||||
from fdroidserver.exception import FDroidException, VCSException, NoSubmodulesException, \
|
from fdroidserver.exception import FDroidException, VCSException, NoSubmodulesException, \
|
||||||
BuildException, VerificationException, MetaDataException
|
BuildException, VerificationException, MetaDataException
|
||||||
from .asynchronousfilereader import AsynchronousFileReader
|
from .asynchronousfilereader import AsynchronousFileReader
|
||||||
@ -549,7 +550,7 @@ def read_config():
|
|||||||
if os.path.exists(CONFIG_FILE):
|
if os.path.exists(CONFIG_FILE):
|
||||||
logging.debug(_("Reading '{config_file}'").format(config_file=CONFIG_FILE))
|
logging.debug(_("Reading '{config_file}'").format(config_file=CONFIG_FILE))
|
||||||
with open(CONFIG_FILE, encoding='utf-8') as fp:
|
with open(CONFIG_FILE, encoding='utf-8') as fp:
|
||||||
config = yaml.safe_load(fp)
|
config = yaml.load(fp)
|
||||||
if not config:
|
if not config:
|
||||||
config = {}
|
config = {}
|
||||||
config_type_check(CONFIG_FILE, config)
|
config_type_check(CONFIG_FILE, config)
|
||||||
@ -706,7 +707,7 @@ def load_localized_config(name, repodir):
|
|||||||
if len(f.parts) == 2:
|
if len(f.parts) == 2:
|
||||||
locale = DEFAULT_LOCALE
|
locale = DEFAULT_LOCALE
|
||||||
with open(f, encoding="utf-8") as fp:
|
with open(f, encoding="utf-8") as fp:
|
||||||
elem = yaml.safe_load(fp)
|
elem = yaml.load(fp)
|
||||||
if not isinstance(elem, dict):
|
if not isinstance(elem, dict):
|
||||||
msg = _('{path} is not "key: value" dict, but a {datatype}!')
|
msg = _('{path} is not "key: value" dict, but a {datatype}!')
|
||||||
raise TypeError(msg.format(path=f, datatype=type(elem).__name__))
|
raise TypeError(msg.format(path=f, datatype=type(elem).__name__))
|
||||||
@ -4229,7 +4230,9 @@ def write_to_config(thisconfig, key, value=None):
|
|||||||
lines[-1] += '\n'
|
lines[-1] += '\n'
|
||||||
|
|
||||||
pattern = re.compile(r'^[\s#]*' + key + r':.*\n')
|
pattern = re.compile(r'^[\s#]*' + key + r':.*\n')
|
||||||
repl = yaml.dump({key: value})
|
with ruamel.yaml.compat.StringIO() as fp:
|
||||||
|
yaml_dumper.dump({key: value}, fp)
|
||||||
|
repl = fp.getvalue()
|
||||||
|
|
||||||
# If we replaced this line once, we make sure won't be a
|
# If we replaced this line once, we make sure won't be a
|
||||||
# second instance of this line for this key in the document.
|
# second instance of this line for this key in the document.
|
||||||
|
@ -26,7 +26,6 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import ruamel.yaml
|
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
@ -45,6 +44,7 @@ from . import metadata
|
|||||||
from . import net
|
from . import net
|
||||||
from . import signindex
|
from . import signindex
|
||||||
from fdroidserver.common import ANTIFEATURES_CONFIG_NAME, CATEGORIES_CONFIG_NAME, CONFIG_CONFIG_NAME, MIRRORS_CONFIG_NAME, RELEASECHANNELS_CONFIG_NAME, DEFAULT_LOCALE, FDroidPopen, FDroidPopenBytes, load_stats_fdroid_signing_key_fingerprints
|
from fdroidserver.common import ANTIFEATURES_CONFIG_NAME, CATEGORIES_CONFIG_NAME, CONFIG_CONFIG_NAME, MIRRORS_CONFIG_NAME, RELEASECHANNELS_CONFIG_NAME, DEFAULT_LOCALE, FDroidPopen, FDroidPopenBytes, load_stats_fdroid_signing_key_fingerprints
|
||||||
|
from fdroidserver._yaml import yaml
|
||||||
from fdroidserver.exception import FDroidException, VerificationException
|
from fdroidserver.exception import FDroidException, VerificationException
|
||||||
|
|
||||||
|
|
||||||
@ -1445,7 +1445,7 @@ def add_mirrors_to_repodict(repo_section, repodict):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
with mirrors_yml.open() as fp:
|
with mirrors_yml.open() as fp:
|
||||||
mirrors_config = ruamel.yaml.YAML(typ='safe').load(fp)
|
mirrors_config = yaml.load(fp)
|
||||||
if not isinstance(mirrors_config, list):
|
if not isinstance(mirrors_config, list):
|
||||||
msg = _('{path} is not list, but a {datatype}!')
|
msg = _('{path} is not list, but a {datatype}!')
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
|
@ -24,9 +24,8 @@ import urllib.parse
|
|||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import ruamel.yaml
|
|
||||||
|
|
||||||
from . import _, common, metadata, rewritemeta
|
from . import _, common, metadata, rewritemeta
|
||||||
|
from fdroidserver._yaml import yaml
|
||||||
|
|
||||||
config = None
|
config = None
|
||||||
|
|
||||||
@ -853,7 +852,7 @@ def lint_config(arg):
|
|||||||
passed = False
|
passed = False
|
||||||
|
|
||||||
with path.open() as fp:
|
with path.open() as fp:
|
||||||
data = ruamel.yaml.YAML(typ='safe').load(fp)
|
data = yaml.load(fp)
|
||||||
common.config_type_check(arg, data)
|
common.config_type_check(arg, data)
|
||||||
|
|
||||||
if path.name == mirrors_name:
|
if path.name == mirrors_name:
|
||||||
|
@ -31,6 +31,7 @@ from collections import OrderedDict
|
|||||||
from . import common
|
from . import common
|
||||||
from . import _
|
from . import _
|
||||||
from .exception import MetaDataException
|
from .exception import MetaDataException
|
||||||
|
from ._yaml import yaml
|
||||||
|
|
||||||
srclibs = None
|
srclibs = None
|
||||||
warnings_action = None
|
warnings_action = None
|
||||||
@ -472,7 +473,6 @@ def parse_yaml_srclib(metadatapath):
|
|||||||
|
|
||||||
with metadatapath.open("r", encoding="utf-8") as f:
|
with metadatapath.open("r", encoding="utf-8") as f:
|
||||||
try:
|
try:
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
data = yaml.load(f)
|
data = yaml.load(f)
|
||||||
if type(data) is not dict:
|
if type(data) is not dict:
|
||||||
if platform.system() == 'Windows':
|
if platform.system() == 'Windows':
|
||||||
@ -709,8 +709,7 @@ def parse_yaml_metadata(mf):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
yamldata = common.yaml.load(mf)
|
||||||
yamldata = yaml.load(mf)
|
|
||||||
except ruamel.yaml.YAMLError as e:
|
except ruamel.yaml.YAMLError as e:
|
||||||
_warn_or_exception(
|
_warn_or_exception(
|
||||||
_("could not parse '{path}'").format(path=mf.name)
|
_("could not parse '{path}'").format(path=mf.name)
|
||||||
@ -1249,19 +1248,24 @@ def _app_to_yaml(app):
|
|||||||
def write_yaml(mf, app):
|
def write_yaml(mf, app):
|
||||||
"""Write metadata in yaml format.
|
"""Write metadata in yaml format.
|
||||||
|
|
||||||
|
This requires the 'rt' round trip dumper to maintain order and needs
|
||||||
|
custom indent settings, so it needs to instantiate its own YAML
|
||||||
|
instance. Therefore, this function deliberately avoids using any of
|
||||||
|
the common YAML parser setups.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
mf
|
mf
|
||||||
active file discriptor for writing
|
active file discriptor for writing
|
||||||
app
|
app
|
||||||
app metadata to written to the yaml file
|
app metadata to written to the YAML file
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_del_duplicated_NoSourceSince(app)
|
_del_duplicated_NoSourceSince(app)
|
||||||
yaml_app = _app_to_yaml(app)
|
yaml_app = _app_to_yaml(app)
|
||||||
yaml = ruamel.yaml.YAML()
|
yamlmf = ruamel.yaml.YAML(typ='rt')
|
||||||
yaml.indent(mapping=2, sequence=4, offset=2)
|
yamlmf.indent(mapping=2, sequence=4, offset=2)
|
||||||
yaml.dump(yaml_app, stream=mf)
|
yamlmf.dump(yaml_app, stream=mf)
|
||||||
|
|
||||||
|
|
||||||
def write_metadata(metadatapath, app):
|
def write_metadata(metadatapath, app):
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
AllowedAPKSigningKeys: []
|
AllowedAPKSigningKeys: []
|
||||||
AntiFeatures:
|
AntiFeatures:
|
||||||
UpstreamNonFree: {}
|
UpstreamNonFree: {}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
AllowedAPKSigningKeys: []
|
AllowedAPKSigningKeys: []
|
||||||
AntiFeatures:
|
AntiFeatures:
|
||||||
NoSourceSince:
|
NoSourceSince:
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
AllowedAPKSigningKeys: []
|
AllowedAPKSigningKeys: []
|
||||||
AntiFeatures: {}
|
AntiFeatures: {}
|
||||||
ArchivePolicy: null
|
ArchivePolicy: null
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
AllowedAPKSigningKeys: []
|
AllowedAPKSigningKeys: []
|
||||||
AntiFeatures: {}
|
AntiFeatures: {}
|
||||||
ArchivePolicy: null
|
ArchivePolicy: null
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
AllowedAPKSigningKeys: []
|
AllowedAPKSigningKeys: []
|
||||||
AntiFeatures: {}
|
AntiFeatures: {}
|
||||||
ArchivePolicy: 9
|
ArchivePolicy: 9
|
||||||
|
@ -17,7 +17,6 @@ import tempfile
|
|||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
import textwrap
|
import textwrap
|
||||||
import yaml
|
|
||||||
import gzip
|
import gzip
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
@ -32,6 +31,7 @@ import fdroidserver.common
|
|||||||
import fdroidserver.metadata
|
import fdroidserver.metadata
|
||||||
from .shared_test_code import TmpCwd, mkdtemp
|
from .shared_test_code import TmpCwd, mkdtemp
|
||||||
from fdroidserver.common import ANTIFEATURES_CONFIG_NAME, CATEGORIES_CONFIG_NAME
|
from fdroidserver.common import ANTIFEATURES_CONFIG_NAME, CATEGORIES_CONFIG_NAME
|
||||||
|
from fdroidserver._yaml import yaml, yaml_dumper
|
||||||
from fdroidserver.exception import FDroidException, VCSException,\
|
from fdroidserver.exception import FDroidException, VCSException,\
|
||||||
MetaDataException, VerificationException
|
MetaDataException, VerificationException
|
||||||
from fdroidserver.looseversion import LooseVersion
|
from fdroidserver.looseversion import LooseVersion
|
||||||
@ -77,6 +77,26 @@ class CommonTest(unittest.TestCase):
|
|||||||
if os.path.exists(self.tmpdir):
|
if os.path.exists(self.tmpdir):
|
||||||
shutil.rmtree(self.tmpdir)
|
shutil.rmtree(self.tmpdir)
|
||||||
|
|
||||||
|
def test_yaml_1_2(self):
|
||||||
|
"""Return a ruamel.yaml instance that supports YAML 1.2
|
||||||
|
|
||||||
|
There should be no "Norway Problem", and other things like this:
|
||||||
|
https://yaml.org/spec/1.2.2/ext/changes/
|
||||||
|
|
||||||
|
YAML 1.2 says "underlines _ cannot be used within numerical
|
||||||
|
values", but ruamel.yaml seems to ignore that. 1_0 should be a
|
||||||
|
string, but it is read as a 10.
|
||||||
|
|
||||||
|
"""
|
||||||
|
os.chdir(self.testdir)
|
||||||
|
yaml12file = Path('YAML 1.2.yml')
|
||||||
|
yaml12file.write_text('[true, no, 0b010, 010, 0o10, "\\/"]', encoding='utf-8')
|
||||||
|
with yaml12file.open() as fp:
|
||||||
|
self.assertEqual(
|
||||||
|
[True, 'no', 2, 10, 8, '/'],
|
||||||
|
yaml.load(fp),
|
||||||
|
)
|
||||||
|
|
||||||
def test_parse_human_readable_size(self):
|
def test_parse_human_readable_size(self):
|
||||||
for k, v in (
|
for k, v in (
|
||||||
(9827, 9827),
|
(9827, 9827),
|
||||||
@ -417,7 +437,7 @@ class CommonTest(unittest.TestCase):
|
|||||||
metadata['RepoType'] = 'git'
|
metadata['RepoType'] = 'git'
|
||||||
metadata['Repo'] = git_url
|
metadata['Repo'] = git_url
|
||||||
with open(os.path.join('metadata', packageName + '.yml'), 'w') as fp:
|
with open(os.path.join('metadata', packageName + '.yml'), 'w') as fp:
|
||||||
yaml.dump(metadata, fp)
|
yaml_dumper.dump(metadata, fp)
|
||||||
|
|
||||||
gitrepo = os.path.join(self.tmpdir, 'build', packageName)
|
gitrepo = os.path.join(self.tmpdir, 'build', packageName)
|
||||||
vcs0 = fdroidserver.common.getvcs('git', git_url, gitrepo)
|
vcs0 = fdroidserver.common.getvcs('git', git_url, gitrepo)
|
||||||
@ -1913,7 +1933,7 @@ class CommonTest(unittest.TestCase):
|
|||||||
os.chdir(self.tmpdir)
|
os.chdir(self.tmpdir)
|
||||||
teststr = '/πÇÇ现代通用字-български-عربي1/ö/yml'
|
teststr = '/πÇÇ现代通用字-български-عربي1/ö/yml'
|
||||||
with open(fdroidserver.common.CONFIG_FILE, 'w', encoding='utf-8') as fp:
|
with open(fdroidserver.common.CONFIG_FILE, 'w', encoding='utf-8') as fp:
|
||||||
yaml.dump({'apksigner': teststr}, fp)
|
yaml_dumper.dump({'apksigner': teststr}, fp)
|
||||||
self.assertTrue(os.path.exists(fdroidserver.common.CONFIG_FILE))
|
self.assertTrue(os.path.exists(fdroidserver.common.CONFIG_FILE))
|
||||||
config = fdroidserver.common.read_config()
|
config = fdroidserver.common.read_config()
|
||||||
self.assertEqual(teststr, config.get('apksigner'))
|
self.assertEqual(teststr, config.get('apksigner'))
|
||||||
@ -1937,7 +1957,7 @@ class CommonTest(unittest.TestCase):
|
|||||||
def test_with_config_yml_is_not_mixed_type(self):
|
def test_with_config_yml_is_not_mixed_type(self):
|
||||||
os.chdir(self.tmpdir)
|
os.chdir(self.tmpdir)
|
||||||
Path(fdroidserver.common.CONFIG_FILE).write_text('k: v\napksigner = /bin/apk')
|
Path(fdroidserver.common.CONFIG_FILE).write_text('k: v\napksigner = /bin/apk')
|
||||||
with self.assertRaises(yaml.scanner.ScannerError):
|
with self.assertRaises(ruamel.yaml.scanner.ScannerError):
|
||||||
fdroidserver.common.read_config()
|
fdroidserver.common.read_config()
|
||||||
|
|
||||||
def test_config_perm_warning(self):
|
def test_config_perm_warning(self):
|
||||||
@ -2613,7 +2633,7 @@ class CommonTest(unittest.TestCase):
|
|||||||
' -providerClass sun.security.pkcs11.SunPKCS11'
|
' -providerClass sun.security.pkcs11.SunPKCS11'
|
||||||
' -providerArg opensc-fdroid.cfg'
|
' -providerArg opensc-fdroid.cfg'
|
||||||
}
|
}
|
||||||
yaml.dump(d, fp)
|
yaml_dumper.dump(d, fp)
|
||||||
config = fdroidserver.common.read_config()
|
config = fdroidserver.common.read_config()
|
||||||
fdroidserver.common.config = config
|
fdroidserver.common.config = config
|
||||||
self.assertTrue(isinstance(d['smartcardoptions'], str))
|
self.assertTrue(isinstance(d['smartcardoptions'], str))
|
||||||
@ -2829,21 +2849,21 @@ class CommonTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_parse_mirrors_config_str(self):
|
def test_parse_mirrors_config_str(self):
|
||||||
s = 'foo@example.com:/var/www'
|
s = 'foo@example.com:/var/www'
|
||||||
mirrors = ruamel.yaml.YAML(typ='safe').load("""'%s'""" % s)
|
mirrors = yaml.load("""'%s'""" % s)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[{'url': s}], fdroidserver.common.parse_mirrors_config(mirrors)
|
[{'url': s}], fdroidserver.common.parse_mirrors_config(mirrors)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_parse_mirrors_config_list(self):
|
def test_parse_mirrors_config_list(self):
|
||||||
s = 'foo@example.com:/var/www'
|
s = 'foo@example.com:/var/www'
|
||||||
mirrors = ruamel.yaml.YAML(typ='safe').load("""- '%s'""" % s)
|
mirrors = yaml.load("""- '%s'""" % s)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[{'url': s}], fdroidserver.common.parse_mirrors_config(mirrors)
|
[{'url': s}], fdroidserver.common.parse_mirrors_config(mirrors)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_parse_mirrors_config_dict(self):
|
def test_parse_mirrors_config_dict(self):
|
||||||
s = 'foo@example.com:/var/www'
|
s = 'foo@example.com:/var/www'
|
||||||
mirrors = ruamel.yaml.YAML(typ='safe').load("""- url: '%s'""" % s)
|
mirrors = yaml.load("""- url: '%s'""" % s)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[{'url': s}], fdroidserver.common.parse_mirrors_config(mirrors)
|
[{'url': s}], fdroidserver.common.parse_mirrors_config(mirrors)
|
||||||
)
|
)
|
||||||
|
@ -11,13 +11,12 @@ from datetime import datetime, timezone
|
|||||||
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
|
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from ruamel.yaml import YAML
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from androguard.core.bytecodes.apk import get_apkid # androguard <4
|
from androguard.core.bytecodes.apk import get_apkid # androguard <4
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
from androguard.core.apk import get_apkid
|
from androguard.core.apk import get_apkid
|
||||||
|
|
||||||
|
from fdroidserver._yaml import yaml, yaml_dumper
|
||||||
from .shared_test_code import mkdir_testfiles
|
from .shared_test_code import mkdir_testfiles
|
||||||
|
|
||||||
# TODO: port generic tests that use index.xml to index-v2 (test that
|
# TODO: port generic tests that use index.xml to index-v2 (test that
|
||||||
@ -81,7 +80,6 @@ class IntegrationTest(unittest.TestCase):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def update_yaml(path, items, replace=False):
|
def update_yaml(path, items, replace=False):
|
||||||
"""Update a .yml file, e.g. config.yml, with the given items."""
|
"""Update a .yml file, e.g. config.yml, with the given items."""
|
||||||
yaml = YAML()
|
|
||||||
doc = {}
|
doc = {}
|
||||||
if not replace:
|
if not replace:
|
||||||
try:
|
try:
|
||||||
@ -91,7 +89,7 @@ class IntegrationTest(unittest.TestCase):
|
|||||||
pass
|
pass
|
||||||
doc.update(items)
|
doc.update(items)
|
||||||
with open(path, "w") as f:
|
with open(path, "w") as f:
|
||||||
yaml.dump(doc, f)
|
yaml_dumper.dump(doc, f)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def remove_lines(path, unwanted_strings):
|
def remove_lines(path, unwanted_strings):
|
||||||
|
@ -7,13 +7,12 @@ import tempfile
|
|||||||
import unittest
|
import unittest
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import ruamel.yaml
|
|
||||||
|
|
||||||
from .shared_test_code import mkdtemp
|
from .shared_test_code import mkdtemp
|
||||||
|
|
||||||
import fdroidserver.common
|
import fdroidserver.common
|
||||||
import fdroidserver.lint
|
import fdroidserver.lint
|
||||||
import fdroidserver.metadata
|
import fdroidserver.metadata
|
||||||
|
from fdroidserver._yaml import yaml_dumper
|
||||||
|
|
||||||
basedir = Path(__file__).parent
|
basedir = Path(__file__).parent
|
||||||
|
|
||||||
@ -365,40 +364,41 @@ class LintTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_lint_config_basic_mirrors_yml(self):
|
def test_lint_config_basic_mirrors_yml(self):
|
||||||
os.chdir(self.testdir)
|
os.chdir(self.testdir)
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
with Path('mirrors.yml').open('w') as fp:
|
with Path('mirrors.yml').open('w') as fp:
|
||||||
yaml.dump([{'url': 'https://example.com/fdroid/repo'}], fp)
|
yaml_dumper.dump([{'url': 'https://example.com/fdroid/repo'}], fp)
|
||||||
self.assertTrue(fdroidserver.lint.lint_config('mirrors.yml'))
|
self.assertTrue(fdroidserver.lint.lint_config('mirrors.yml'))
|
||||||
|
|
||||||
def test_lint_config_mirrors_yml_kenya_countryCode(self):
|
def test_lint_config_mirrors_yml_kenya_countryCode(self):
|
||||||
os.chdir(self.testdir)
|
os.chdir(self.testdir)
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
with Path('mirrors.yml').open('w') as fp:
|
with Path('mirrors.yml').open('w') as fp:
|
||||||
yaml.dump([{'url': 'https://foo.com/fdroid/repo', 'countryCode': 'KE'}], fp)
|
yaml_dumper.dump(
|
||||||
|
[{'url': 'https://foo.com/fdroid/repo', 'countryCode': 'KE'}], fp
|
||||||
|
)
|
||||||
self.assertTrue(fdroidserver.lint.lint_config('mirrors.yml'))
|
self.assertTrue(fdroidserver.lint.lint_config('mirrors.yml'))
|
||||||
|
|
||||||
def test_lint_config_mirrors_yml_invalid_countryCode(self):
|
def test_lint_config_mirrors_yml_invalid_countryCode(self):
|
||||||
"""WV is "indeterminately reserved" so it should never be used."""
|
"""WV is "indeterminately reserved" so it should never be used."""
|
||||||
os.chdir(self.testdir)
|
os.chdir(self.testdir)
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
with Path('mirrors.yml').open('w') as fp:
|
with Path('mirrors.yml').open('w') as fp:
|
||||||
yaml.dump([{'url': 'https://foo.com/fdroid/repo', 'countryCode': 'WV'}], fp)
|
yaml_dumper.dump(
|
||||||
|
[{'url': 'https://foo.com/fdroid/repo', 'countryCode': 'WV'}], fp
|
||||||
|
)
|
||||||
self.assertFalse(fdroidserver.lint.lint_config('mirrors.yml'))
|
self.assertFalse(fdroidserver.lint.lint_config('mirrors.yml'))
|
||||||
|
|
||||||
def test_lint_config_mirrors_yml_alpha3_countryCode(self):
|
def test_lint_config_mirrors_yml_alpha3_countryCode(self):
|
||||||
"""Only ISO 3166-1 alpha 2 are supported"""
|
"""Only ISO 3166-1 alpha 2 are supported"""
|
||||||
os.chdir(self.testdir)
|
os.chdir(self.testdir)
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
with Path('mirrors.yml').open('w') as fp:
|
with Path('mirrors.yml').open('w') as fp:
|
||||||
yaml.dump([{'url': 'https://de.com/fdroid/repo', 'countryCode': 'DEU'}], fp)
|
yaml_dumper.dump(
|
||||||
|
[{'url': 'https://de.com/fdroid/repo', 'countryCode': 'DEU'}], fp
|
||||||
|
)
|
||||||
self.assertFalse(fdroidserver.lint.lint_config('mirrors.yml'))
|
self.assertFalse(fdroidserver.lint.lint_config('mirrors.yml'))
|
||||||
|
|
||||||
def test_lint_config_mirrors_yml_one_invalid_countryCode(self):
|
def test_lint_config_mirrors_yml_one_invalid_countryCode(self):
|
||||||
"""WV is "indeterminately reserved" so it should never be used."""
|
"""WV is "indeterminately reserved" so it should never be used."""
|
||||||
os.chdir(self.testdir)
|
os.chdir(self.testdir)
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
with Path('mirrors.yml').open('w') as fp:
|
with Path('mirrors.yml').open('w') as fp:
|
||||||
yaml.dump(
|
yaml_dumper.dump(
|
||||||
[
|
[
|
||||||
{'url': 'https://bar.com/fdroid/repo', 'countryCode': 'BA'},
|
{'url': 'https://bar.com/fdroid/repo', 'countryCode': 'BA'},
|
||||||
{'url': 'https://foo.com/fdroid/repo', 'countryCode': 'FO'},
|
{'url': 'https://foo.com/fdroid/repo', 'countryCode': 'FO'},
|
||||||
|
@ -17,6 +17,7 @@ import fdroidserver
|
|||||||
from fdroidserver import metadata
|
from fdroidserver import metadata
|
||||||
from fdroidserver.exception import MetaDataException
|
from fdroidserver.exception import MetaDataException
|
||||||
from fdroidserver.common import DEFAULT_LOCALE
|
from fdroidserver.common import DEFAULT_LOCALE
|
||||||
|
from fdroidserver._yaml import yaml
|
||||||
from .shared_test_code import TmpCwd, mkdtemp
|
from .shared_test_code import TmpCwd, mkdtemp
|
||||||
|
|
||||||
|
|
||||||
@ -178,7 +179,6 @@ class MetadataTest(unittest.TestCase):
|
|||||||
def test_valid_funding_yml_regex(self):
|
def test_valid_funding_yml_regex(self):
|
||||||
"""Check the regex can find all the cases"""
|
"""Check the regex can find all the cases"""
|
||||||
with (basedir / 'funding-usernames.yaml').open() as fp:
|
with (basedir / 'funding-usernames.yaml').open() as fp:
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
data = yaml.load(fp)
|
data = yaml.load(fp)
|
||||||
|
|
||||||
for k, entries in data.items():
|
for k, entries in data.items():
|
||||||
@ -207,7 +207,6 @@ class MetadataTest(unittest.TestCase):
|
|||||||
fdroidserver.common.config = config
|
fdroidserver.common.config = config
|
||||||
fdroidserver.metadata.warnings_action = None
|
fdroidserver.metadata.warnings_action = None
|
||||||
|
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
apps = fdroidserver.metadata.read_metadata()
|
apps = fdroidserver.metadata.read_metadata()
|
||||||
for appid in (
|
for appid in (
|
||||||
'app.with.special.build.params',
|
'app.with.special.build.params',
|
||||||
@ -337,7 +336,6 @@ class MetadataTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_normalize_type_string_sha256(self):
|
def test_normalize_type_string_sha256(self):
|
||||||
"""SHA-256 values are TYPE_STRING, which YAML can parse as decimal ints."""
|
"""SHA-256 values are TYPE_STRING, which YAML can parse as decimal ints."""
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
for v in range(1, 1000):
|
for v in range(1, 1000):
|
||||||
s = '%064d' % (v * (10**51))
|
s = '%064d' % (v * (10**51))
|
||||||
self.assertEqual(s, metadata._normalize_type_string(yaml.load(s)))
|
self.assertEqual(s, metadata._normalize_type_string(yaml.load(s)))
|
||||||
@ -378,7 +376,6 @@ class MetadataTest(unittest.TestCase):
|
|||||||
def test_normalize_type_list(self):
|
def test_normalize_type_list(self):
|
||||||
"""TYPE_LIST is always a list of strings, no matter what YAML thinks."""
|
"""TYPE_LIST is always a list of strings, no matter what YAML thinks."""
|
||||||
k = 'placeholder'
|
k = 'placeholder'
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
self.assertEqual(['1.0'], metadata._normalize_type_list(k, 1.0))
|
self.assertEqual(['1.0'], metadata._normalize_type_list(k, 1.0))
|
||||||
self.assertEqual(['1234567890'], metadata._normalize_type_list(k, 1234567890))
|
self.assertEqual(['1234567890'], metadata._normalize_type_list(k, 1234567890))
|
||||||
self.assertEqual(['false'], metadata._normalize_type_list(k, False))
|
self.assertEqual(['false'], metadata._normalize_type_list(k, False))
|
||||||
@ -441,7 +438,6 @@ class MetadataTest(unittest.TestCase):
|
|||||||
def test_post_parse_yaml_metadata_0padding_sha256(self):
|
def test_post_parse_yaml_metadata_0padding_sha256(self):
|
||||||
"""SHA-256 values are strings, but YAML 1.2 will read some as decimal ints."""
|
"""SHA-256 values are strings, but YAML 1.2 will read some as decimal ints."""
|
||||||
v = '0027293472934293872934729834729834729834729834792837487293847926'
|
v = '0027293472934293872934729834729834729834729834792837487293847926'
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
yamldata = yaml.load('AllowedAPKSigningKeys: ' + v)
|
yamldata = yaml.load('AllowedAPKSigningKeys: ' + v)
|
||||||
metadata.post_parse_yaml_metadata(yamldata)
|
metadata.post_parse_yaml_metadata(yamldata)
|
||||||
self.assertEqual(yamldata['AllowedAPKSigningKeys'], [v])
|
self.assertEqual(yamldata['AllowedAPKSigningKeys'], [v])
|
||||||
@ -2287,7 +2283,6 @@ class PostMetadataParseTest(unittest.TestCase):
|
|||||||
maximum of two leading zeros, but this will handle more.
|
maximum of two leading zeros, but this will handle more.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
yaml = ruamel.yaml.YAML(typ='safe', pure=True)
|
|
||||||
str_sha256 = '0000000000000498456908409534729834729834729834792837487293847926'
|
str_sha256 = '0000000000000498456908409534729834729834729834792837487293847926'
|
||||||
sha256 = yaml.load('a: ' + str_sha256)['a']
|
sha256 = yaml.load('a: ' + str_sha256)['a']
|
||||||
self.assertEqual(*self._post_metadata_parse_app_int(sha256, int(str_sha256)))
|
self.assertEqual(*self._post_metadata_parse_app_int(sha256, int(str_sha256)))
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import ruamel.yaml
|
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
@ -24,6 +23,7 @@ from fdroidserver import publish
|
|||||||
from fdroidserver import common
|
from fdroidserver import common
|
||||||
from fdroidserver import metadata
|
from fdroidserver import metadata
|
||||||
from fdroidserver import signatures
|
from fdroidserver import signatures
|
||||||
|
from fdroidserver._yaml import yaml
|
||||||
from fdroidserver.exception import FDroidException
|
from fdroidserver.exception import FDroidException
|
||||||
from .shared_test_code import mkdtemp, VerboseFalseOptions
|
from .shared_test_code import mkdtemp, VerboseFalseOptions
|
||||||
|
|
||||||
@ -116,7 +116,6 @@ class PublishTest(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
self.assertEqual(expected, common.load_stats_fdroid_signing_key_fingerprints())
|
self.assertEqual(expected, common.load_stats_fdroid_signing_key_fingerprints())
|
||||||
|
|
||||||
yaml = ruamel.yaml.YAML(typ='safe')
|
|
||||||
with open(common.CONFIG_FILE) as fp:
|
with open(common.CONFIG_FILE) as fp:
|
||||||
config = yaml.load(fp)
|
config = yaml.load(fp)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user