1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-11-10 17:30:11 +01:00

Merge branch 'config_lint' into 'master'

lint: check for valid keys in config files

See merge request fdroid/fdroidserver!1516
This commit is contained in:
Hans-Christoph Steiner 2024-09-05 11:28:49 +00:00
commit 88a23f3a33
2 changed files with 57 additions and 11 deletions

View File

@ -16,19 +16,17 @@
# You should have received a copy of the GNU Affero General Public Licen # You should have received a copy of the GNU Affero General Public Licen
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from argparse import ArgumentParser
import difflib import difflib
import platform
import re import re
import sys import sys
import platform
import ruamel.yaml
import urllib.parse import urllib.parse
from argparse import ArgumentParser
from pathlib import Path from pathlib import Path
from . import _ import ruamel.yaml
from . import common
from . import metadata from . import _, common, metadata, rewritemeta
from . import rewritemeta
config = None config = None
@ -588,7 +586,7 @@ def check_app_field_types(app):
fieldtype=v.__class__.__name__, fieldtype=v.__class__.__name__,
) )
) )
elif t == metadata.TYPE_STRING and not type(v) in (str, bool, dict): elif t == metadata.TYPE_STRING and type(v) not in (str, bool, dict):
yield ( yield (
_( _(
"{appid}: {field} must be a '{type}', but it is a '{fieldtype}'!" "{appid}: {field} must be a '{type}', but it is a '{fieldtype}'!"
@ -751,6 +749,12 @@ def check_certificate_pinned_binaries(app):
def lint_config(arg): def lint_config(arg):
path = Path(arg) path = Path(arg)
passed = True passed = True
mirrors_name = f'{common.MIRRORS_CONFIG_NAME}.yml'
config_name = f'{common.CONFIG_CONFIG_NAME}.yml'
categories_name = f'{common.CATEGORIES_CONFIG_NAME}.yml'
antifeatures_name = f'{common.ANTIFEATURES_CONFIG_NAME}.yml'
yamllintresult = common.run_yamllint(path) yamllintresult = common.run_yamllint(path)
if yamllintresult: if yamllintresult:
print(yamllintresult) print(yamllintresult)
@ -760,7 +764,7 @@ def lint_config(arg):
data = ruamel.yaml.YAML(typ='safe').load(fp) data = ruamel.yaml.YAML(typ='safe').load(fp)
common.config_type_check(arg, data) common.config_type_check(arg, data)
if path.name == 'mirrors.yml': if path.name == mirrors_name:
import pycountry import pycountry
valid_country_codes = [c.alpha_2 for c in pycountry.countries] valid_country_codes = [c.alpha_2 for c in pycountry.countries]
@ -781,6 +785,34 @@ def lint_config(arg):
msg += ' ' msg += ' '
msg += _('Did you mean {code}?').format(code=', '.join(sorted(m))) msg += _('Did you mean {code}?').format(code=', '.join(sorted(m)))
print(msg) print(msg)
elif path.name in (config_name, categories_name, antifeatures_name):
for key in data:
if path.name == config_name and key not in ('archive', 'repo'):
passed = False
print(
_('ERROR: {key} in {path} is not "archive" or "repo"!').format(
key=key, path=path
)
)
allowed_keys = ['name']
if path.name in [config_name, antifeatures_name]:
allowed_keys.append('description')
# only for source strings currently
if path.parent.name == 'config':
allowed_keys.append('icon')
for subkey in data[key]:
if subkey not in allowed_keys:
passed = False
print(
_(
'ERROR: {key}:{subkey} in {path} is not in allowed keys: {allowed_keys}!'
).format(
key=key,
subkey=subkey,
path=path,
allowed_keys=', '.join(allowed_keys),
)
)
return passed return passed

View File

@ -4,23 +4,25 @@
import logging import logging
import os import os
import ruamel.yaml
import shutil import shutil
import sys import sys
import tempfile import tempfile
import unittest import unittest
from pathlib import Path from pathlib import Path
import ruamel.yaml
localmodule = Path(__file__).resolve().parent.parent localmodule = Path(__file__).resolve().parent.parent
print('localmodule: ' + str(localmodule)) print('localmodule: ' + str(localmodule))
if localmodule not in sys.path: if localmodule not in sys.path:
sys.path.insert(0, str(localmodule)) sys.path.insert(0, str(localmodule))
from testcommon import mkdtemp, parse_args_for_test
import fdroidserver.common import fdroidserver.common
import fdroidserver.lint import fdroidserver.lint
import fdroidserver.metadata import fdroidserver.metadata
from fdroidserver.common import CATEGORIES_CONFIG_NAME from fdroidserver.common import CATEGORIES_CONFIG_NAME
from testcommon import mkdtemp, parse_args_for_test
class LintTest(unittest.TestCase): class LintTest(unittest.TestCase):
@ -437,6 +439,18 @@ class LintTest(unittest.TestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
fdroidserver.lint.lint_config('mirrors.yml') fdroidserver.lint.lint_config('mirrors.yml')
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')
self.assertFalse(fdroidserver.lint.lint_config('config/config.yml'))
def test_lint_invalid_localized_config_keys(self):
os.chdir(self.testdir)
Path('config/en').mkdir(parents=True)
Path('config/en/antiFeatures.yml').write_text('NonFreeNet:\n icon: test.png')
self.assertFalse(fdroidserver.lint.lint_config('config/en/antiFeatures.yml'))
def test_check_certificate_pinned_binaries_empty(self): def test_check_certificate_pinned_binaries_empty(self):
fdroidserver.common.config = {} fdroidserver.common.config = {}
app = fdroidserver.metadata.App() app = fdroidserver.metadata.App()