66Outputs:
77- codestate_pr_report.md : Markdown content suitable for PR comments
88- codestate_pr_report.html : An HTML artifact with a detailed table
9+ - codestate_pr_report.json : Machine-readable payload
910
1011Env (recommended):
1112- BASE_SHA: base commit SHA (e.g., github.event.pull_request.base.sha)
1516
1617import os
1718import re
18- import sys
1919import json
20- import html
20+ import html as html_lib
2121import shlex
2222import subprocess
2323from pathlib import Path
@@ -40,15 +40,13 @@ def run(cmd: str) -> Tuple[int, str, str]:
4040
4141
4242def list_changed_files (base : str , head : str ) -> List [str ]:
43- # Include Added, Copied, Modified, Renamed, Type changed, Unmerged, Unknown, Broken
4443 cmd = f"git diff --name-only --diff-filter=ACMRTUXB { shlex .quote (base )} ..{ shlex .quote (head )} "
4544 code , out , err = run (cmd )
4645 if code != 0 :
4746 print (f"::warning::Failed to list changed files: { err .strip ()} \n Falling back to 'git diff --name-only' on HEAD." )
4847 code , out , _ = run ("git diff --name-only" )
4948 files = [line .strip () for line in out .splitlines () if line .strip ()]
50- # Filter only supported extensions and existing files
51- result = []
49+ result : List [str ] = []
5250 for f in files :
5351 p = Path (f )
5452 if p .suffix .lower () in SUPPORTED_EXTS and p .exists () and p .is_file ():
@@ -60,24 +58,20 @@ def estimate_comment_lines(ext: str, lines: List[str]) -> int:
6058 ext = ext .lower ()
6159 comment = 0
6260 in_block = False
63- block_start = {'/*' , "'''" , '"""' }
6461 block_end = {'*/' , "'''" , '"""' }
6562 for raw in lines :
6663 s = raw .strip ()
6764 if not s :
6865 continue
6966 if in_block :
7067 comment += 1
71- # Heuristic: close on end token appearance
7268 if any (tok in s for tok in block_end ):
7369 in_block = False
7470 continue
75- # Single-line styles
7671 if ext in {'.py' }:
7772 if s .startswith ('#' ):
7873 comment += 1
7974 continue
80- # Start of docstring block
8175 if s .startswith ("'''" ) or s .startswith ('"""' ):
8276 comment += 1
8377 if not (s .endswith ("'''" ) or s .endswith ('"""' )):
@@ -175,19 +169,16 @@ def render_markdown(by_ext: Dict[str, dict], per_file: List[dict]) -> str:
175169 if not per_file :
176170 return f"{ title } \n \n No supported code changes detected."
177171
178- # Extension summary table
179172 exts = sorted (by_ext .values (), key = lambda x : (- x ['total_lines' ], x ['ext' ]))
180173 header = "| ext | files | lines | comments | functions |\n |---|---:|---:|---:|---:|"
181174 rows = [
182175 f"| { e ['ext' ]} | { e ['file_count' ]} | { e ['total_lines' ]} | { e ['comment_lines' ]} | { e ['function_count' ]} |"
183176 for e in exts
184177 ]
185178
186- # Language distribution bars (by lines)
187179 max_lines = max ((e ['total_lines' ] for e in exts ), default = 0 )
188180 bars = [f"`{ e ['ext' ]} ` { make_bar (e ['total_lines' ], max_lines )} { e ['total_lines' ]} " for e in exts ]
189181
190- # Complexity heatmap (top 10)
191182 top_complex = sorted (per_file , key = lambda x : (- x ['complexity' ], - x ['total_lines' ]))[:10 ]
192183 max_c = int (max ((f ['complexity' ] for f in top_complex ), default = 0 ))
193184 heat = [
@@ -212,7 +203,7 @@ def render_markdown(by_ext: Dict[str, dict], per_file: List[dict]) -> str:
212203 md .append ("- No files to display" )
213204 md .append ("" )
214205 md .append (
215- "> Generated by CodeState PR reporter. An HTML report has been uploaded as a workflow artifact."
206+ "> Generated by CodeState PR reporter. An HTML report has been uploaded as a workflow artifact and published to GitHub Pages ."
216207 )
217208 return "\n " .join (md )
218209
@@ -221,7 +212,7 @@ def render_html(by_ext: Dict[str, dict], per_file: List[dict]) -> str:
221212 html_rows = []
222213 for f in sorted (per_file , key = lambda x : (- x ['total_lines' ], x ['file' ])):
223214 html_rows .append (
224- f"<tr><td>{ html .escape (f ['file' ])} </td><td>{ html .escape (f ['ext' ])} </td>"
215+ f"<tr><td>{ html_lib .escape (f ['file' ])} </td><td>{ html_lib .escape (f ['ext' ])} </td>"
225216 f"<td style='text-align:right'>{ f ['total_lines' ]} </td>"
226217 f"<td style='text-align:right'>{ f ['comment_lines' ]} </td>"
227218 f"<td style='text-align:right'>{ f ['function_count' ]} </td>"
@@ -230,18 +221,18 @@ def render_html(by_ext: Dict[str, dict], per_file: List[dict]) -> str:
230221 ext_rows = []
231222 for e in sorted (by_ext .values (), key = lambda x : (- x ['total_lines' ], x ['ext' ])):
232223 ext_rows .append (
233- f"<tr><td>{ html .escape (e ['ext' ])} </td>"
224+ f"<tr><td>{ html_lib .escape (e ['ext' ])} </td>"
234225 f"<td style='text-align:right'>{ e ['file_count' ]} </td>"
235226 f"<td style='text-align:right'>{ e ['total_lines' ]} </td>"
236227 f"<td style='text-align:right'>{ e ['comment_lines' ]} </td>"
237228 f"<td style='text-align:right'>{ e ['function_count' ]} </td></tr>"
238229 )
239230 return f"""
240231<!doctype html>
241- <html lang="en">
232+ <html lang=\ " en\ " >
242233<head>
243- <meta charset="utf-8" />
244- <meta name="viewport" content="width=device-width, initial-scale=1" />
234+ <meta charset=\ " utf-8\ " />
235+ <meta name=\ " viewport\ " content=\ " width=device-width, initial-scale=1\ " />
245236 <title>CodeState PR Report</title>
246237 <style>
247238 body {{ font-family: -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; padding: 16px; }}
@@ -254,7 +245,7 @@ def render_html(by_ext: Dict[str, dict], per_file: List[dict]) -> str:
254245 </head>
255246<body>
256247 <h2>CodeState PR Report — Changed Files</h2>
257- <p class="muted">This artifact lists per-file and per-extension metrics for the current pull request.</p>
248+ <p class=\ " muted\ " >This artifact lists per-file and per-extension metrics for the current pull request.</p>
258249 <table>
259250 <caption>Summary by extension</caption>
260251 <thead>
@@ -273,7 +264,7 @@ def render_html(by_ext: Dict[str, dict], per_file: List[dict]) -> str:
273264 { '' .join (html_rows )}
274265 </tbody>
275266 </table>
276- <p class="muted">Generated by CodeState PR reporter.</p>
267+ <p class=\ " muted\ " >Generated by CodeState PR reporter.</p>
277268</body>
278269</html>
279270"""
@@ -283,8 +274,6 @@ def main() -> int:
283274 base = os .getenv ('BASE_SHA' ) or os .getenv ('GITHUB_BASE_SHA' ) or ''
284275 head = os .getenv ('HEAD_SHA' ) or os .getenv ('GITHUB_HEAD_SHA' ) or ''
285276 if not base or not head :
286- # Try to infer base from origin or default branch
287- # Fallback to HEAD~1..HEAD
288277 code , out , _ = run ("git rev-parse HEAD" )
289278 head = out .strip () if out .strip () else head
290279 code , out , _ = run ("git rev-parse HEAD~1" )
@@ -299,7 +288,6 @@ def main() -> int:
299288 Path ('codestate_pr_report.md' ).write_text (md , encoding = 'utf-8' )
300289 Path ('codestate_pr_report.html' ).write_text (html_content , encoding = 'utf-8' )
301290
302- # Optional: also write a compact JSON for future automation
303291 payload = {
304292 'base' : base , 'head' : head , 'file_count' : len (per_file ),
305293 'by_ext' : list (by_ext .values ()), 'per_file' : per_file ,
0 commit comments