biome: 1.9.2 -> 1.9.3 (#349335)
[NixPkgs.git] / maintainers / scripts / remove-old-aliases.py
blob3c5f8edc50adfe6faee1d5a75b318f83819b7c22
1 #!/usr/bin/env nix-shell
2 #!nix-shell -i python3 -p "python3.withPackages(ps: with ps; [ ])" nix
3 """
4 A program to remove old aliases or convert old aliases to throws
5 Example usage:
6 ./maintainers/scripts/remove-old-aliases.py --year 2018 --file ./pkgs/top-level/aliases.nix
8 Check this file with mypy after every change!
9 $ mypy --strict maintainers/scripts/remove-old-aliases.py
10 """
11 import argparse
12 import shutil
13 import subprocess
14 from datetime import date as datetimedate
15 from datetime import datetime
16 from pathlib import Path
19 def 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"
25 arg_parser.add_argument(
26 "--month",
27 type=int,
28 default=1,
29 help="operate on aliases older than $year-$month",
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",
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"
40 return arg_parser.parse_args()
43 def 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] = []
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
65 if (
66 my_date is None
67 or my_date > cutoffdate
68 or "preserve, reason:" in line.lower()
70 continue
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)
82 return (
83 date_older_list,
84 date_sep_line_list,
85 date_older_throw_list,
89 def 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
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.")
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')}"
113 converted_list.append((line, converted))
115 return converted_list
118 def 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)
137 return text_to_write
140 def 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,
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";',
164 temp_aliasfile.unlink()
165 return
166 shutil.move(f"{aliasfile}.raliases", aliasfile)
167 print(f"{aliasfile} modified! please verify with 'git diff'.")
170 def main() -> None:
171 """main"""
172 args = process_args()
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()
178 txt: list[str] = (aliasfile.read_text(encoding="utf-8")).splitlines()
180 date_older_list: list[str] = []
181 date_sep_line_list: list[str] = []
182 date_older_throw_list: list[str] = []
184 date_older_list, date_sep_line_list, date_older_throw_list = get_date_lists(
185 txt, cutoffdate, only_throws
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)
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)
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)
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
209 write_file(aliasfile, text_to_write)
212 if __name__ == "__main__":
213 main()