diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 59b58e34..23942b49 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -42,6 +42,8 @@ import urllib.request import zipfile import tempfile import json +import yamllint.config +import yamllint.linter # TODO change to only import defusedxml once its installed everywhere try: @@ -3734,3 +3736,18 @@ def force_exit(exitvalue=0): sys.stdout.flush() sys.stderr.flush() os._exit(exitvalue) + + +YAML_LINT_CONFIG = {'extends': 'default', + 'rules': {'document-start': 'disable', + 'line-length': 'disable', + 'truthy': 'disable'}} + + +def run_yamllint(path, indent=0): + result = [] + with open(path, 'r', encoding='utf-8') as f: + problems = yamllint.linter.run(f, yamllint.config.YamlLintConfig(json.dumps(YAML_LINT_CONFIG))) + for problem in problems: + result.append(' ' * indent + path + ':' + str(problem.line) + ': ' + problem.message) + return '\n'.join(result) diff --git a/fdroidserver/lint.py b/fdroidserver/lint.py index 276bf555..4e097b2a 100644 --- a/fdroidserver/lint.py +++ b/fdroidserver/lint.py @@ -600,6 +600,13 @@ def main(): if app.Disabled: continue + if len(options.appid) > 0: + ymlpath = os.path.join('metadata', appid + '.yml') + if os.path.isfile(ymlpath): + yamllintresult = common.run_yamllint(ymlpath) + if yamllintresult != '': + print(yamllintresult) + app_check_funcs = [ check_app_field_types, check_regexes, diff --git a/fdroidserver/metadata.py b/fdroidserver/metadata.py index 3f79db43..99764ac4 100644 --- a/fdroidserver/metadata.py +++ b/fdroidserver/metadata.py @@ -747,7 +747,7 @@ def parse_txt_srclib(metadatapath): return thisinfo -def parse_yml_srclib(metadatapath): +def parse_yaml_srclib(metadatapath): thisinfo = {'RepoType': '', 'Repo': '', @@ -765,9 +765,11 @@ def parse_yml_srclib(metadatapath): data = yaml.load(f, Loader=SafeLoader) except yaml.error.YAMLError as e: warn_or_exception(_("Invalid srclib metadata: could not " - "parse '{file}'" - .format(file=metadatapath)), - e) + "parse '{file}'") + .format(file=metadatapath) + '\n' + + fdroidserver.common.run_yamllint(metadatapath, + indent=4), + cause=e) return thisinfo for key in data.keys(): @@ -820,7 +822,7 @@ def read_srclibs(): for metadatapath in sorted(glob.glob(os.path.join(srcdir, '*.yml'))): srclibname = os.path.basename(metadatapath[:-4]) - srclibs[srclibname] = parse_yml_srclib(metadatapath) + srclibs[srclibname] = parse_yaml_srclib(metadatapath) def read_metadata(xref=True, check_vcs=[], refresh=True, sort_by_time=False): @@ -1102,7 +1104,14 @@ def parse_json_metadata(mf, app): def parse_yaml_metadata(mf, app): - yamldata = yaml.load(mf, Loader=SafeLoader) + try: + yamldata = yaml.load(mf, Loader=SafeLoader) + except yaml.YAMLError as e: + warn_or_exception(_("could not parse '{path}'") + .format(path=mf.name) + '\n' + + fdroidserver.common.run_yamllint(mf.name, + indent=4), + cause=e) deprecated_in_yaml = ['Provides'] diff --git a/tests/metadata.TestCase b/tests/metadata.TestCase index dedbdd3f..d52cd991 100755 --- a/tests/metadata.TestCase +++ b/tests/metadata.TestCase @@ -652,7 +652,7 @@ class MetadataTest(unittest.TestCase): "'android.library=true\\ntarget=android-19' > project.properties"}, srclib) - def test_parse_yml_srclib_unknown_key(self): + def test_parse_yaml_srclib_unknown_key(self): fdroidserver.metadata.warnings_action = 'error' with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with open('test.yml', 'w', encoding='utf-8') as f: @@ -665,17 +665,17 @@ class MetadataTest(unittest.TestCase): "Invalid srclib metadata: " "unknown key 'Evil' in " "'test.yml'"): - fdroidserver.metadata.parse_yml_srclib('test.yml') + fdroidserver.metadata.parse_yaml_srclib('test.yml') - def test_parse_yml_srclib_does_not_exists(self): + def test_parse_yaml_srclib_does_not_exists(self): fdroidserver.metadata.warnings_action = 'error' with self.assertRaisesRegex(MetaDataException, "Invalid scrlib metadata: " "'non/existent-test-srclib.yml' " "does not exist"): - fdroidserver.metadata.parse_yml_srclib('non/existent-test-srclib.yml') + fdroidserver.metadata.parse_yaml_srclib('non/existent-test-srclib.yml') - def test_parse_yml_srclib_simple(self): + def test_parse_yaml_srclib_simple(self): fdroidserver.metadata.warnings_action = 'error' with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with open('simple.yml', 'w', encoding='utf-8') as f: @@ -684,14 +684,14 @@ class MetadataTest(unittest.TestCase): RepoType: git Repo: https://git.host/repo.git ''')) - srclib = fdroidserver.metadata.parse_yml_srclib('simple.yml') + srclib = fdroidserver.metadata.parse_yaml_srclib('simple.yml') self.assertDictEqual({'Repo': 'https://git.host/repo.git', 'RepoType': 'git', 'Subdir': None, 'Prepare': None}, srclib) - def test_parse_yml_srclib_simple_with_blanks(self): + def test_parse_yaml_srclib_simple_with_blanks(self): fdroidserver.metadata.warnings_action = 'error' with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with open('simple.yml', 'w', encoding='utf-8') as f: @@ -706,14 +706,14 @@ class MetadataTest(unittest.TestCase): Prepare: ''')) - srclib = fdroidserver.metadata.parse_yml_srclib('simple.yml') + srclib = fdroidserver.metadata.parse_yaml_srclib('simple.yml') self.assertDictEqual({'Repo': 'https://git.host/repo.git', 'RepoType': 'git', 'Subdir': [''], 'Prepare': ''}, srclib) - def test_parse_yml_srclib_Changelog_cketti(self): + def test_parse_yaml_srclib_Changelog_cketti(self): fdroidserver.metadata.warnings_action = 'error' with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with open('Changelog-cketti.yml', 'w', encoding='utf-8') as f: @@ -724,7 +724,7 @@ class MetadataTest(unittest.TestCase): Subdir: library,ckChangeLog/src/main Prepare: "[ -f project.properties ] || echo 'source.dir=java' > ant.properties && echo -e 'android.library=true\\\\ntarget=android-19' > project.properties" ''')) - srclib = fdroidserver.metadata.parse_yml_srclib('Changelog-cketti.yml') + srclib = fdroidserver.metadata.parse_yaml_srclib('Changelog-cketti.yml') self.assertDictEqual(srclib, {'Repo': 'https://github.com/cketti/ckChangeLog', 'RepoType': 'git',