@@ -26,6 +26,7 @@ def format_file(file: Path, check: bool, formatter) -> bool:
2626 if not formatter :
2727 return True # Unsupported file type, skip
2828
29+ formatted = True
2930 try :
3031 cmd = []
3132 if formatter .startswith ("clang-format" ):
@@ -43,7 +44,7 @@ def format_file(file: Path, check: bool, formatter) -> bool:
4344 print (
4445 f"Use { Fore .CYAN } { formatter } -style=file -i { file } { Style .RESET_ALL } to format it."
4546 )
46- return False
47+ formatted = False
4748 else :
4849 subprocess .run (
4950 cmd ,
@@ -60,14 +61,14 @@ def format_file(file: Path, check: bool, formatter) -> bool:
6061 cmd ,
6162 capture_output = True ,
6263 text = True ,
63- check = True ,
64+ check = False ,
6465 )
6566 if process .returncode != 0 :
6667 print (f"{ Fore .YELLOW } { file } is not formatted.{ Style .RESET_ALL } " )
6768 print (
6869 f"Use { Fore .CYAN } { formatter } { file } { Style .RESET_ALL } to format it."
6970 )
70- return False
71+ formatted = False
7172 else :
7273 subprocess .run (
7374 cmd ,
@@ -76,13 +77,74 @@ def format_file(file: Path, check: bool, formatter) -> bool:
7677 check = True ,
7778 )
7879 print (f"{ Fore .CYAN } Formatted: { file } { Style .RESET_ALL } " )
80+
81+ ruff_cmd = ["ruff" , "check" , file ]
82+ try :
83+ if check :
84+ process = subprocess .run (
85+ ruff_cmd ,
86+ capture_output = True ,
87+ text = True ,
88+ check = False ,
89+ )
90+ if process .returncode != 0 :
91+ print (f"{ Fore .YELLOW } { file } has ruff issues.{ Style .RESET_ALL } " )
92+ print (
93+ f"Use { Fore .CYAN } ruff check --fix { file } { Style .RESET_ALL } to fix it."
94+ )
95+ formatted = False
96+ else :
97+ ruff_cmd .insert (2 , "--fix" )
98+ process = subprocess .run (
99+ ruff_cmd ,
100+ capture_output = True ,
101+ text = True ,
102+ check = False ,
103+ )
104+ if process .returncode != 0 :
105+ print (f"{ Fore .RED } Ruff failed for { file } .{ Style .RESET_ALL } " )
106+ if process .stdout :
107+ print (process .stdout , end = "" )
108+ if process .stderr :
109+ print (process .stderr , end = "" )
110+ formatted = False
111+ else :
112+ print (f"{ Fore .CYAN } Ruff fixed: { file } { Style .RESET_ALL } " )
113+ except FileNotFoundError :
114+ print (
115+ f"{ Fore .RED } Formatter ruff not found, { file } skipped.{ Style .RESET_ALL } "
116+ )
117+ formatted = False
79118 except FileNotFoundError :
80119 print (
81120 f"{ Fore .RED } Formatter { formatter } not found, { file } skipped.{ Style .RESET_ALL } "
82121 )
122+ formatted = False
83123 except subprocess .CalledProcessError as e :
84124 print (f"{ Fore .RED } Formatter { formatter } failed: { e } { Style .RESET_ALL } " )
125+ formatted = False
126+
127+ if not ensure_single_trailing_newline (file , check ):
128+ formatted = False
129+
130+ return formatted
131+
132+
133+ def ensure_single_trailing_newline (file : Path , check : bool ) -> bool :
134+ """Ensure the file ends with exactly one LF byte."""
135+ content = file .read_bytes ()
136+ fixed = content .rstrip (b"\r \n " ) + b"\n "
137+ if content == fixed :
138+ return True
139+
140+ if check :
141+ print (
142+ f"{ Fore .YELLOW } { file } does not end with exactly one newline.{ Style .RESET_ALL } "
143+ )
144+ return False
85145
146+ file .write_bytes (fixed )
147+ print (f"{ Fore .CYAN } Fixed trailing newline: { file } { Style .RESET_ALL } " )
86148 return True
87149
88150
@@ -119,7 +181,6 @@ def git_modified_since_ref(ref):
119181
120182def list_files (paths ):
121183 """Recursively get all files under the specified paths"""
122- files = []
123184 for path in paths :
124185 if path .is_file ():
125186 yield path
0 commit comments