forked from Mirror/pmbootstrap
107 lines
3.9 KiB
Python
107 lines
3.9 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
|
|
# 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])
|
|
|
|
return config
|
|
|
|
|
|
def serialize(config: Config, skip_defaults: bool = 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 == "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) -> None:
|
|
"""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)
|