1
0
Fork 1
mirror of https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git synced 2025-07-12 19:09:56 +03:00
pmbootstrap/pmb/config/file.py
Oliver Smith 9dfa89c58c
Use pmbootstrap_v3.cfg as config file (MR 2350)
Using pmbootstrap v3 with the old config can cause problems, for example
when having $WORK in the pmaports dir instead of the actual work path.
This is not supported anymore by v3 to reduce complexity. The format of
how mirrors are stored in the config also has changed.

Use a separate config file, so users can go back from v3 to 2.3.x if
they need to (for figuring out a regression) and so users won't run into
bugs when moving from 2.3.x to v3.
2024-07-10 20:06:25 +02:00

125 lines
4.7 KiB
Python

# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
from pathlib import Path, PosixPath
from pmb.helpers import logging
import configparser
import os
from pmb.core import Config
def load(path: Path) -> Config:
config = Config()
cfg = configparser.ConfigParser()
if os.path.isfile(path):
cfg.read(path)
if "pmbootstrap" not in cfg:
cfg["pmbootstrap"] = {}
if "providers" not in cfg:
cfg["providers"] = {}
for key in Config.__dict__.keys():
if key == "providers":
setattr(config, key, cfg["providers"])
if key == "mirrors" and key in cfg:
for subkey in Config.mirrors.keys():
if subkey in cfg["mirrors"]:
setattr(config, f"mirrors.{subkey}", cfg["mirrors"][subkey])
# default values won't be set in the config file
if key not in cfg["pmbootstrap"]:
continue
elif key == "mirror_alpine":
# DEPRCATED
config.mirrors["alpine"] = cfg["pmbootstrap"]["mirror_alpine"]
continue
# Handle whacky type conversions
elif key == "mirrors_postmarketos":
mirrors = cfg["pmbootstrap"]["mirrors_postmarketos"].split(",")
if len(mirrors) > 1:
logging.warning("Multiple mirrors are not supported, using the last one")
config.mirrors["pmaports"] = mirrors[-1].strip("/master")
# Convert strings to paths
elif type(getattr(Config, key)) is PosixPath:
setattr(config, key, Path(cfg["pmbootstrap"][key]))
# Yeah this really sucks and there isn't a better way to do it without external
# libraries
elif isinstance(getattr(Config, key), list) and isinstance(
getattr(Config, key)[0], PosixPath
):
value = cfg["pmbootstrap"][key]
if not value:
setattr(config, key, value)
else:
setattr(config, key, [Path(p) for p in value.split(",")])
elif isinstance(getattr(Config, key), bool):
setattr(config, key, cfg["pmbootstrap"][key].lower() == "true")
elif key in cfg["pmbootstrap"]:
setattr(config, key, cfg["pmbootstrap"][key])
# One time migration "mirror_alpine" -> mirrors.alpine
if "mirror_alpine" in cfg["pmbootstrap"] or "mirrors_postmarketos" in cfg["pmbootstrap"]:
logging.info("Migrating config file to 3.0 format.")
save(path, config)
return config
def serialize(config: Config, skip_defaults=True) -> configparser.ConfigParser:
"""Serialize the config object into a ConfigParser to write it out
in the pmbootstrap_v3.cfg INI format.
:param config: The config object to serialize
:param skip_defaults: Skip writing out default values
"""
cfg = configparser.ConfigParser()
cfg["pmbootstrap"] = {}
cfg["providers"] = {}
cfg["mirrors"] = {}
# .keys() flat maps dictionaries like config.mirrors with
# dotted notation
for key in Config.keys():
# If the default value hasn't changed then don't write out,
# this makes it possible to update the default, otherwise
# we wouldn't be able to tell if the user overwrote it.
if skip_defaults and Config.get_default(key) == getattr(config, key):
continue
if key == "mirror_alpine" or key == "mirrors_postmarketos":
# DEPRECATED: skip these
continue
if key == "providers":
cfg["providers"] = config.providers
elif key.startswith("mirrors."):
_key = key.split(".")[1]
cfg["mirrors"][_key] = getattr(config, key)
# Convert strings to paths
elif type(getattr(Config, key)) is PosixPath:
cfg["pmbootstrap"][key] = str(getattr(config, key))
elif isinstance(getattr(Config, key), list) and isinstance(
getattr(Config, key)[0], PosixPath
):
cfg["pmbootstrap"][key] = ",".join(os.fspath(p) for p in getattr(config, key))
elif isinstance(getattr(Config, key), bool):
cfg["pmbootstrap"][key] = str(getattr(config, key))
else:
cfg["pmbootstrap"][key] = str(getattr(config, key))
return cfg
# FIXME: we should have distinct Config and ConfigFile types
def save(output: Path, config: Config):
"""Save the config object to the specified path.
IMPORTANT: The global config (available via get_context().config)
has invocation arguments merged into it. Do NOT call save() with
the global config object."""
logging.debug(f"Save config: {output}")
output.parent.mkdir(parents=True, exist_ok=True)
output.touch(0o700, exist_ok=True)
cfg = serialize(config)
with output.open("w") as handle:
cfg.write(handle)