at master 6.9 kB view raw
1#!/usr/bin/env nix-shell 2#!nix-shell -i python3 -p "python3.withPackages(ps: with ps; [ ])" nix 3""" 4A program to remove old aliases or convert old aliases to throws 5Example usage: 6./maintainers/scripts/remove-old-aliases.py --year 2018 --file ./pkgs/top-level/aliases.nix 7 8Check this file with mypy after every change! 9$ mypy --strict maintainers/scripts/remove-old-aliases.py 10""" 11import argparse 12import shutil 13import subprocess 14from datetime import date as datetimedate 15from datetime import datetime 16from pathlib import Path 17 18 19def process_args() -> argparse.Namespace: 20 """process args""" 21 arg_parser = argparse.ArgumentParser() 22 arg_parser.add_argument( 23 "--year", required=True, type=int, help="operate on aliases older than $year" 24 ) 25 arg_parser.add_argument( 26 "--month", 27 type=int, 28 default=1, 29 help="operate on aliases older than $year-$month", 30 ) 31 arg_parser.add_argument( 32 "--only-throws", 33 action="store_true", 34 help="only operate on throws. e.g remove throws older than $date", 35 ) 36 arg_parser.add_argument("--file", required=True, type=Path, help="alias file") 37 arg_parser.add_argument( 38 "--dry-run", action="store_true", help="don't modify files, only print results" 39 ) 40 return arg_parser.parse_args() 41 42 43def get_date_lists( 44 txt: list[str], cutoffdate: datetimedate, only_throws: bool 45) -> tuple[list[str], list[str], list[str]]: 46 """get a list of lines in which the date is older than $cutoffdate""" 47 date_older_list: list[str] = [] 48 date_older_throw_list: list[str] = [] 49 date_sep_line_list: list[str] = [] 50 51 for lineno, line in enumerate(txt, start=1): 52 line = line.rstrip() 53 my_date = None 54 for string in line.split(): 55 string = string.strip(":") 56 try: 57 # strip ':' incase there is a string like 2019-11-01: 58 my_date = datetime.strptime(string, "%Y-%m-%d").date() 59 except ValueError: 60 try: 61 my_date = datetime.strptime(string, "%Y-%m").date() 62 except ValueError: 63 continue 64 65 if ( 66 my_date is None 67 or my_date > cutoffdate 68 or "preserve, reason:" in line.lower() 69 ): 70 continue 71 72 if "=" not in line: 73 date_sep_line_list.append(f"{lineno} {line}") 74 # 'if' lines could be complicated 75 elif "if " in line and "if =" not in line: 76 print(f"RESOLVE MANUALLY {line}") 77 elif "throw" in line: 78 date_older_throw_list.append(line) 79 elif not only_throws: 80 date_older_list.append(line) 81 82 return ( 83 date_older_list, 84 date_sep_line_list, 85 date_older_throw_list, 86 ) 87 88 89def convert_to_throw(date_older_list: list[str]) -> list[tuple[str, str]]: 90 """convert a list of lines to throws""" 91 converted_list = [] 92 for line in date_older_list.copy(): 93 indent: str = " " * (len(line) - len(line.lstrip())) 94 before_equal = "" 95 after_equal = "" 96 try: 97 before_equal, after_equal = (x.strip() for x in line.split("=", maxsplit=2)) 98 except ValueError as err: 99 print(err, line, "\n") 100 date_older_list.remove(line) 101 continue 102 103 alias = before_equal 104 alias_unquoted = before_equal.strip('"') 105 replacement = next(x.strip(";:") for x in after_equal.split()) 106 replacement = replacement.removeprefix("pkgs.") 107 108 converted = ( 109 f"{indent}{alias} = throw \"'{alias_unquoted}' has been" 110 f" renamed to/replaced by '{replacement}'\";" 111 f" # Converted to throw {datetime.today().strftime('%Y-%m-%d')}" 112 ) 113 converted_list.append((line, converted)) 114 115 return converted_list 116 117 118def generate_text_to_write( 119 txt: list[str], 120 date_older_list: list[str], 121 converted_to_throw: list[tuple[str, str]], 122 date_older_throw_list: list[str], 123) -> list[str]: 124 """generate a list of text to be written to the aliasfile""" 125 text_to_write: list[str] = [] 126 for line in txt: 127 text_to_append: str = "" 128 if converted_to_throw: 129 for tupl in converted_to_throw: 130 if line == tupl[0]: 131 text_to_append = f"{tupl[1]}\n" 132 if line not in date_older_list and line not in date_older_throw_list: 133 text_to_append = f"{line}\n" 134 if text_to_append: 135 text_to_write.append(text_to_append) 136 137 return text_to_write 138 139 140def write_file( 141 aliasfile: Path, 142 text_to_write: list[str], 143) -> None: 144 """write file""" 145 temp_aliasfile = Path(f"{aliasfile}.raliases") 146 with open(temp_aliasfile, "w", encoding="utf-8") as far: 147 for line in text_to_write: 148 far.write(line) 149 print("\nChecking the syntax of the new aliasfile") 150 try: 151 subprocess.run( 152 ["nix-instantiate", "--eval", temp_aliasfile], 153 check=True, 154 stdout=subprocess.DEVNULL, 155 ) 156 except subprocess.CalledProcessError: 157 print( 158 "\nSyntax check failed,", 159 "there may have been a line which only has\n" 160 'aliasname = "reason why";\n' 161 "when it should have been\n" 162 'aliasname = throw "reason why";', 163 ) 164 temp_aliasfile.unlink() 165 return 166 shutil.move(f"{aliasfile}.raliases", aliasfile) 167 print(f"{aliasfile} modified! please verify with 'git diff'.") 168 169 170def main() -> None: 171 """main""" 172 args = process_args() 173 174 only_throws = args.only_throws 175 aliasfile = Path(args.file).absolute() 176 cutoffdate = (datetime.strptime(f"{args.year}-{args.month}-01", "%Y-%m-%d")).date() 177 178 txt: list[str] = (aliasfile.read_text(encoding="utf-8")).splitlines() 179 180 date_older_list: list[str] = [] 181 date_sep_line_list: list[str] = [] 182 date_older_throw_list: list[str] = [] 183 184 date_older_list, date_sep_line_list, date_older_throw_list = get_date_lists( 185 txt, cutoffdate, only_throws 186 ) 187 188 converted_to_throw: list[tuple[str, str]] = [] 189 if date_older_list: 190 converted_to_throw = convert_to_throw(date_older_list) 191 print(" Will be converted to throws. ".center(100, "-")) 192 for l_n in date_older_list: 193 print(l_n) 194 195 if date_older_throw_list: 196 print(" Will be removed. ".center(100, "-")) 197 for l_n in date_older_throw_list: 198 print(l_n) 199 200 if date_sep_line_list: 201 print(" On separate line, resolve manually. ".center(100, "-")) 202 for l_n in date_sep_line_list: 203 print(l_n) 204 205 if not args.dry_run: 206 text_to_write = generate_text_to_write( 207 txt, date_older_list, converted_to_throw, date_older_throw_list 208 ) 209 write_file(aliasfile, text_to_write) 210 211 212if __name__ == "__main__": 213 main()