mirror of
https://github.com/f-droid/fdroidserver.git
synced 2025-11-14 03:00:29 +03:00
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
15 changed files with 116 additions and 48 deletions
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 ast
|
||||
import gzip
|
||||
import ruamel.yaml
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
|
|
@ -48,7 +49,6 @@ import logging
|
|||
import hashlib
|
||||
import socket
|
||||
import base64
|
||||
import yaml
|
||||
import zipfile
|
||||
import tempfile
|
||||
import json
|
||||
|
|
@ -67,6 +67,7 @@ from zipfile import ZipFile
|
|||
|
||||
import fdroidserver.metadata
|
||||
from fdroidserver import _
|
||||
from fdroidserver._yaml import yaml, yaml_dumper
|
||||
from fdroidserver.exception import FDroidException, VCSException, NoSubmodulesException, \
|
||||
BuildException, VerificationException, MetaDataException
|
||||
from .asynchronousfilereader import AsynchronousFileReader
|
||||
|
|
@ -549,7 +550,7 @@ def read_config():
|
|||
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)
|
||||
config = yaml.load(fp)
|
||||
if not config:
|
||||
config = {}
|
||||
config_type_check(CONFIG_FILE, config)
|
||||
|
|
@ -706,7 +707,7 @@ def load_localized_config(name, repodir):
|
|||
if len(f.parts) == 2:
|
||||
locale = DEFAULT_LOCALE
|
||||
with open(f, encoding="utf-8") as fp:
|
||||
elem = yaml.safe_load(fp)
|
||||
elem = yaml.load(fp)
|
||||
if not isinstance(elem, dict):
|
||||
msg = _('{path} is not "key: value" dict, but a {datatype}!')
|
||||
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'
|
||||
|
||||
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
|
||||
# second instance of this line for this key in the document.
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import json
|
|||
import logging
|
||||
import os
|
||||
import re
|
||||
import ruamel.yaml
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
|
@ -45,6 +44,7 @@ from . import metadata
|
|||
from . import net
|
||||
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._yaml import yaml
|
||||
from fdroidserver.exception import FDroidException, VerificationException
|
||||
|
||||
|
||||
|
|
@ -1445,7 +1445,7 @@ def add_mirrors_to_repodict(repo_section, repodict):
|
|||
)
|
||||
)
|
||||
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):
|
||||
msg = _('{path} is not list, but a {datatype}!')
|
||||
raise TypeError(
|
||||
|
|
|
|||
|
|
@ -24,9 +24,8 @@ import urllib.parse
|
|||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
|
||||
import ruamel.yaml
|
||||
|
||||
from . import _, common, metadata, rewritemeta
|
||||
from fdroidserver._yaml import yaml
|
||||
|
||||
config = None
|
||||
|
||||
|
|
@ -853,7 +852,7 @@ def lint_config(arg):
|
|||
passed = False
|
||||
|
||||
with path.open() as fp:
|
||||
data = ruamel.yaml.YAML(typ='safe').load(fp)
|
||||
data = yaml.load(fp)
|
||||
common.config_type_check(arg, data)
|
||||
|
||||
if path.name == mirrors_name:
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ from collections import OrderedDict
|
|||
from . import common
|
||||
from . import _
|
||||
from .exception import MetaDataException
|
||||
from ._yaml import yaml
|
||||
|
||||
srclibs = None
|
||||
warnings_action = None
|
||||
|
|
@ -472,7 +473,6 @@ def parse_yaml_srclib(metadatapath):
|
|||
|
||||
with metadatapath.open("r", encoding="utf-8") as f:
|
||||
try:
|
||||
yaml = ruamel.yaml.YAML(typ='safe')
|
||||
data = yaml.load(f)
|
||||
if type(data) is not dict:
|
||||
if platform.system() == 'Windows':
|
||||
|
|
@ -709,8 +709,7 @@ def parse_yaml_metadata(mf):
|
|||
|
||||
"""
|
||||
try:
|
||||
yaml = ruamel.yaml.YAML(typ='safe')
|
||||
yamldata = yaml.load(mf)
|
||||
yamldata = common.yaml.load(mf)
|
||||
except ruamel.yaml.YAMLError as e:
|
||||
_warn_or_exception(
|
||||
_("could not parse '{path}'").format(path=mf.name)
|
||||
|
|
@ -1249,19 +1248,24 @@ def _app_to_yaml(app):
|
|||
def write_yaml(mf, app):
|
||||
"""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
|
||||
----------
|
||||
mf
|
||||
active file discriptor for writing
|
||||
app
|
||||
app metadata to written to the yaml file
|
||||
app metadata to written to the YAML file
|
||||
|
||||
"""
|
||||
_del_duplicated_NoSourceSince(app)
|
||||
yaml_app = _app_to_yaml(app)
|
||||
yaml = ruamel.yaml.YAML()
|
||||
yaml.indent(mapping=2, sequence=4, offset=2)
|
||||
yaml.dump(yaml_app, stream=mf)
|
||||
yamlmf = ruamel.yaml.YAML(typ='rt')
|
||||
yamlmf.indent(mapping=2, sequence=4, offset=2)
|
||||
yamlmf.dump(yaml_app, stream=mf)
|
||||
|
||||
|
||||
def write_metadata(metadatapath, app):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue