diff --git a/maintainers/scripts/remove-old-aliases.py b/maintainers/scripts/remove-old-aliases.py new file mode 100755 index 000000000000..5d9398feaa25 --- /dev/null +++ b/maintainers/scripts/remove-old-aliases.py @@ -0,0 +1,202 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i python3 -p "python3.withPackages(ps: with ps; [ ])" nix +""" +A program to remove old aliases or convert old aliases to throws +Example usage: +./maintainers/scripts/remove-old-aliases.py --year 2018 --file ./pkgs/top-level/aliases.nix + +Check this file with mypy after every change! +$ mypy --strict maintainers/scripts/remove-old-aliases.py +""" +import argparse +import shutil +import subprocess +from datetime import date as datetimedate +from datetime import datetime +from pathlib import Path + + +def process_args() -> argparse.Namespace: + """process args""" + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument( + "--year", required=True, type=int, help="operate on aliases older than $year" + ) + arg_parser.add_argument( + "--month", + type=int, + default=1, + help="operate on aliases older than $year-$month", + ) + arg_parser.add_argument("--file", required=True, type=Path, help="alias file") + arg_parser.add_argument( + "--dry-run", action="store_true", help="don't modify files, only print results" + ) + return arg_parser.parse_args() + + +def get_date_lists( + txt: list[str], cutoffdate: datetimedate +) -> tuple[list[str], list[str], list[str]]: + """get a list of lines in which the date is older than $cutoffdate""" + date_older_list: list[str] = [] + date_older_throw_list: list[str] = [] + date_sep_line_list: list[str] = [] + + for lineno, line in enumerate(txt, start=1): + line = line.rstrip() + my_date = None + for string in line.split(): + string = string.strip(":") + try: + # strip ':' incase there is a string like 2019-11-01: + my_date = datetime.strptime(string, "%Y-%m-%d").date() + except ValueError: + try: + my_date = datetime.strptime(string, "%Y-%m").date() + except ValueError: + continue + + if my_date is None or my_date > cutoffdate: + continue + + if "=" not in line: + date_sep_line_list.append(f"{lineno} {line}") + # 'if' lines could be complicated + elif "if " in line and "if =" not in line: + print(f"RESOLVE MANUALLY {line}") + elif "throw" in line: + date_older_throw_list.append(line) + else: + date_older_list.append(line) + + return ( + date_older_list, + date_sep_line_list, + date_older_throw_list, + ) + + +def convert_to_throw(date_older_list: list[str]) -> list[tuple[str, str]]: + """convert a list of lines to throws""" + converted_list = [] + for line in date_older_list.copy(): + indent: str = " " * (len(line) - len(line.lstrip())) + before_equal = "" + after_equal = "" + try: + before_equal, after_equal = (x.strip() for x in line.split("=", maxsplit=2)) + except ValueError as err: + print(err, line, "\n") + date_older_list.remove(line) + continue + + alias = before_equal.strip() + after_equal_list = [x.strip(";:") for x in after_equal.split()] + + converted = ( + f"{indent}{alias} = throw \"'{alias}' has been renamed to/replaced by" + f" '{after_equal_list.pop(0)}'\";" + f' # Converted to throw {datetime.today().strftime("%Y-%m-%d")}' + ) + converted_list.append((line, converted)) + + return converted_list + + +def generate_text_to_write( + txt: list[str], + date_older_list: list[str], + converted_to_throw: list[tuple[str, str]], + date_older_throw_list: list[str], +) -> list[str]: + """generate a list of text to be written to the aliasfile""" + text_to_write: list[str] = [] + for line in txt: + text_to_append: str = "" + if converted_to_throw: + for tupl in converted_to_throw: + if line == tupl[0]: + text_to_append = f"{tupl[1]}\n" + if line not in date_older_list and line not in date_older_throw_list: + text_to_append = f"{line}\n" + if text_to_append: + text_to_write.append(text_to_append) + + return text_to_write + + +def write_file( + aliasfile: Path, + text_to_write: list[str], +) -> None: + """write file""" + temp_aliasfile = Path(f"{aliasfile}.raliases") + with open(temp_aliasfile, "w", encoding="utf-8") as far: + for line in text_to_write: + far.write(line) + print("\nChecking the syntax of the new aliasfile") + try: + subprocess.run( + ["nix-instantiate", "--eval", temp_aliasfile], + check=True, + stdout=subprocess.DEVNULL, + ) + except subprocess.CalledProcessError: + print( + "\nSyntax check failed,", + "there may have been a line which only has\n" + 'aliasname = "reason why";\n' + "when it should have been\n" + 'aliasname = throw "reason why";', + ) + temp_aliasfile.unlink() + return + shutil.move(f"{aliasfile}.raliases", aliasfile) + print(f"{aliasfile} modified! please verify with 'git diff'.") + + +def main() -> None: + """main""" + args = process_args() + + aliasfile = Path(args.file).absolute() + cutoffdate = (datetime.strptime(f"{args.year}-{args.month}-01", "%Y-%m-%d")).date() + + txt: list[str] = (aliasfile.read_text(encoding="utf-8")).splitlines() + + date_older_list: list[str] = [] + date_sep_line_list: list[str] = [] + date_older_throw_list: list[str] = [] + + date_older_list, date_sep_line_list, date_older_throw_list = get_date_lists( + txt, cutoffdate + ) + + converted_to_throw: list[tuple[str, str]] = [] + converted_to_throw = convert_to_throw(date_older_list) + + if date_older_list: + print(" Will be converted to throws. ".center(100, "-")) + for l_n in date_older_list: + print(l_n) + + if date_older_throw_list: + print(" Will be removed. ".center(100, "-")) + for l_n in date_older_throw_list: + print(l_n) + + if date_sep_line_list: + print(" On separate line, resolve manually. ".center(100, "-")) + for l_n in date_sep_line_list: + print(l_n) + + if not args.dry_run: + text_to_write = generate_text_to_write( + txt, date_older_list, converted_to_throw, date_older_throw_list + ) + write_file(aliasfile, text_to_write) + + +if __name__ == "__main__": + main() diff --git a/pkgs/top-level/aliases.nix b/pkgs/top-level/aliases.nix index af9e8c60fdda..0277ab295e3c 100644 --- a/pkgs/top-level/aliases.nix +++ b/pkgs/top-level/aliases.nix @@ -35,6 +35,9 @@ in ### Please maintain this list in ASCIIbetical ordering. ### Hint: the "sections" are delimited by ### ### +# A script to convert old aliases to throws and remove old +# throws can be found in './maintainers/scripts/remove-old-aliases.py'. + mapAliases ({ # forceSystem should not be used directly in Nixpkgs. # Added 2018-07-16