File indexing completed on 2024-12-01 08:16:20
0001 """ 0002 Module containing classes for working with configuration 0003 """ 0004 0005 # SPDX-FileCopyrightText: 2020 Jonah BrĂ¼chert <jbb@kaidan.im> 0006 # 0007 # SPDX-License-Identifier: GPL-2.0-or-later 0008 0009 import json 0010 import os 0011 import sys 0012 import subprocess 0013 from enum import Enum, auto 0014 from pathlib import Path 0015 0016 from typing import TextIO, Dict, Optional, Any, Tuple 0017 0018 from appdirs import user_config_dir 0019 from lab.utils import Utils, LogType 0020 0021 0022 class Config: 0023 """ 0024 Class that can load and store settings 0025 0026 Config file layout: 0027 { 0028 "version": 1, 0029 "instances": { 0030 "gitlab.com": { 0031 "auth_type": "token", 0032 "token": "dkasjdlaksjdlkj", 0033 "command": None 0034 }, 0035 "invent.kde.org": { 0036 "auth_type": "command", 0037 "command": "gpg --decrypt", 0038 "token": None 0039 } 0040 } 0041 } 0042 """ 0043 0044 config_path: str = user_config_dir("gitlabconfig") 0045 0046 __file: TextIO 0047 __config: Dict[str, Any] 0048 0049 def __migrate_to_version_1(self) -> None: 0050 if "version" not in self.__config: 0051 Utils.log(LogType.INFO, "Migrating configuration file to version 1") 0052 0053 new_config: Dict[str, Any] = {"version": 1, "instances": {}} 0054 0055 for hostname in self.__config.keys(): 0056 new_config["instances"][hostname] = { 0057 "auth_type": "token", 0058 "token": self.__config[hostname], 0059 } 0060 0061 self.__config = new_config 0062 self.save() 0063 0064 def __init__(self) -> None: 0065 if not os.path.isfile(self.config_path): 0066 old_config_path: str = os.path.expanduser("~/.gitlabconfig") 0067 config_dir = Path(self.config_path).parent 0068 if os.path.isfile(old_config_path): 0069 if not os.path.isdir(config_dir): 0070 os.mkdir(config_dir) 0071 os.rename(old_config_path, self.config_path) 0072 else: 0073 if not os.path.isdir(config_dir): 0074 os.mkdir(config_dir) 0075 with open(self.config_path, "w+") as file: 0076 json.dump({"version": 1, "instances": {}}, file) 0077 file.close() 0078 0079 self.__file = open(self.config_path, "r+") 0080 self.__config = json.load(self.__file) 0081 0082 self.__migrate_to_version_1() 0083 0084 def save(self) -> None: 0085 """ 0086 Save the config to disk. This function has to be manually called, 0087 otherwise the config won't be saved. 0088 """ 0089 self.__file.seek(0) 0090 json.dump(self.__config, self.__file, indent=4) 0091 self.__file.truncate() 0092 self.__file.flush() 0093 0094 def token(self, hostname: str) -> Optional[str]: 0095 """ 0096 Returns the token for a GitLab instance. 0097 If none was found, it returns None 0098 """ 0099 if hostname in self.__config["instances"]: 0100 # Command case 0101 if ( 0102 "auth_type" in self.__config["instances"][hostname] 0103 and self.__config["instances"][hostname]["auth_type"] == "command" 0104 ): 0105 return ( 0106 subprocess.check_output( 0107 self.__config["instances"][hostname]["command"], shell=True 0108 ) 0109 .decode() 0110 .strip() 0111 ) 0112 0113 # Token case 0114 token = self.__config["instances"][hostname]["token"] 0115 if isinstance(token, str): 0116 return token 0117 0118 return None 0119 0120 def set_token(self, hostname: str, token: str) -> None: 0121 """ 0122 Sets the token for a GitLab instance 0123 """ 0124 if hostname not in self.__config["instances"]: 0125 self.__config["instances"][hostname] = {} 0126 0127 self.__config["instances"][hostname]["token"] = token 0128 self.__config["instances"][hostname]["auth_type"] = "token" 0129 0130 def set_auth_command(self, hostname: str, command: str) -> None: 0131 """ 0132 Sets the command that git-lab runs when it needs an access token 0133 """ 0134 if hostname not in self.__config["instances"]: 0135 self.__config["instances"][hostname] = {} 0136 0137 self.__config["instances"][hostname]["command"] = command 0138 self.__config["instances"][hostname]["auth_type"] = "command" 0139 0140 def instances(self) -> Tuple[str, ...]: 0141 """ 0142 Returns the list of known instances 0143 """ 0144 try: 0145 return tuple(self.__config["instances"].keys()) 0146 except KeyError: 0147 return () 0148 0149 return () 0150 0151 0152 class Workflow(Enum): 0153 """ 0154 Different values for workflow 0155 """ 0156 0157 # Never reorder this values! The config file stores the actual numbers. 0158 FORK = auto() # push merge request branches to a fork of the upstream repository 0159 WORKBRANCH = auto() # push merge request branches to the upstream repository 0160 0161 0162 class RepositoryConfig: 0163 """ 0164 Per-repository config file 0165 """ 0166 0167 config_path: str 0168 __file: TextIO 0169 __config: Dict[str, Any] 0170 0171 def __init__(self) -> None: 0172 repository_path: Optional[str] = Utils.find_dotgit(os.getcwd()) 0173 if repository_path: 0174 self.config_path = repository_path + os.path.sep + ".git" + os.path.sep + "gitlabconfig" 0175 else: 0176 Utils.log(LogType.ERROR, "Current directory is not a git repository") 0177 sys.exit(1) 0178 0179 if not os.path.isfile(self.config_path): 0180 with open(self.config_path, "w+") as file: 0181 json.dump({}, file) 0182 file.close() 0183 0184 self.__file = open(self.config_path, "r+") 0185 self.__config = json.load(self.__file) 0186 0187 def workflow(self) -> Workflow: 0188 """ 0189 get the workflow used for the repository. Defaults to RepositoryConfig.workflow.fork 0190 """ 0191 value: Any 0192 try: 0193 value = self.__config["workflow"] 0194 except KeyError: 0195 return Workflow.FORK 0196 0197 return Workflow(value) 0198 0199 def set_workflow(self, workflow: Workflow) -> None: 0200 """ 0201 Set the workflow to one of RepositoryConfig.Workflow 0202 """ 0203 0204 # Make sure not to corrupt the config file with invalid numbers 0205 if not isinstance(workflow, Workflow): 0206 raise TypeError() 0207 0208 self.__config["workflow"] = workflow.value 0209 0210 def save(self) -> None: 0211 """ 0212 Save the config to disk. This function has to be manually called, 0213 otherwise the config won't be saved. 0214 """ 0215 self.__file.seek(0) 0216 json.dump(self.__config, self.__file, indent=4) 0217 self.__file.truncate() 0218 self.__file.flush()