OpenMiInk/third_party/ci-notary-service/sftpnotary/projects.py
Hellings b8c8ff98e2 Hi
2026-10-16 09:05:56 -04:00

124 lines
4 KiB
Python

# SPDX-License-Identifier: BSD-2-Clause
# SPDX-FileCopyrightText: 2023 KDE e.V.
# SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
import logging
from pathlib import Path
from typing import Any, Dict, IO, List, Optional, Union
import yaml
log = logging.getLogger(__name__)
class Error(Exception):
pass
def readProjectSettings(yamlStream: Union[str, IO]) -> None:
global settings
settings = ProjectSettings(yamlStream)
class ProjectSettings:
# Possible boolean values (same as configparser)
BOOLEAN_STATES = {
"1": True,
"yes": True,
"true": True,
"on": True,
"0": False,
"no": False,
"false": False,
"off": False,
}
def __init__(self, yamlStream: Union[str, IO]):
self._projects: Dict[str, Dict[str, Dict[str, str]]] = {}
self._readSettings(yamlStream)
def _readSettings(self, yamlStream):
try:
# Set "nosec B506" to silence false positive warning "Use of unsafe yaml load.
# Allows instantiation of arbitrary objects. Consider yaml.safe_load()."
# We use yaml.load with BaseLoader which is even safer than SafeLoader.
d = yaml.load(yamlStream, yaml.BaseLoader) # nosec B506
except Exception as e:
log.error("Error: Failed to read project settings.")
log.debug("Error details:", exc_info=e)
return
if d is None:
# PyYAML didn't find any documents in the stream
return
defaultSettings = d.get("defaults", {})
defaultBranches = []
if "branches" in defaultSettings:
defaultBranches = defaultSettings["branches"]
del defaultSettings["branches"]
projects = {}
for p in d:
if p == "defaults":
continue
projects[p] = _readProject(d[p] or {}, defaultBranches, defaultSettings)
self._projects = projects
def exists(self, projectPath: str, branch: Optional[str] = None) -> bool:
if branch is None:
return projectPath in self._projects
return branch in self._projects.get(projectPath, {})
def get(self, projectPath: str, branch: str, key: str, default: Any = None) -> Any:
p = self._projects.get(projectPath)
if p is None:
raise Error(f"Unknown project '{projectPath}'")
b = p.get(branch)
if b is None:
raise Error(f"Unknown branch '{branch}' of project '{projectPath}'")
return b.get(key, default)
def getBool(self, projectPath: str, branch: str, key: str, default: bool) -> bool:
value = self.get(projectPath, branch, key, default)
if isinstance(value, str):
if value.lower() not in self.BOOLEAN_STATES:
raise ValueError("Not a boolean: %s" % value)
value = self.BOOLEAN_STATES[value.lower()]
return value
def getPath(
self, projectPath: str, branch: str, key: str, default: Optional[Union[Path, str]] = None
) -> Optional[Path]:
v = self.get(projectPath, branch, key, default)
if isinstance(v, str):
v = Path(v).expanduser()
return v
def _readProject(projectSettings: dict, defaultBranches: List[str], defaultSettings: dict):
project = {}
branches = projectSettings.get("branches")
if branches is not None:
del projectSettings["branches"]
for b in branches:
# initialize with the default settings
branchSettings = defaultSettings.copy()
# update with the project settings
branchSettings.update(projectSettings.copy())
# update with the branch-specific settings
branchSettings.update(branches[b] or {})
project[b] = branchSettings
# add the default branches
for b in defaultBranches:
if b not in project:
branchSettings = defaultSettings.copy()
branchSettings.update(projectSettings.copy())
project[b] = branchSettings
return project
settings = ProjectSettings("")