Skip to content

Commit c253b7e

Browse files
committed
setup
1 parent caa2281 commit c253b7e

File tree

2 files changed

+63
-24
lines changed

2 files changed

+63
-24
lines changed

.github/workflows/codestate-pr-report.yml

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
name: Generate PR Code Report
1111
runs-on: ubuntu-latest
1212
permissions:
13-
contents: read
13+
contents: write
1414
pull-requests: write
1515
steps:
1616
- name: Checkout
@@ -30,6 +30,57 @@ jobs:
3030
run: |
3131
python tools/codestate_pr_report.py
3232
33+
- name: Build Pages content
34+
env:
35+
PR_NUMBER: ${{ github.event.pull_request.number }}
36+
GITHUB_SHA: ${{ github.sha }}
37+
run: |
38+
set -e
39+
SHORT_SHA=$(git rev-parse --short "$GITHUB_SHA")
40+
OUTDIR="site"
41+
mkdir -p "$OUTDIR"
42+
# Decide sub path
43+
if [ -n "$PR_NUMBER" ]; then
44+
SUBPATH="reports/pr-${PR_NUMBER}/sha-${SHORT_SHA}"
45+
else
46+
SUBPATH="reports/manual/sha-${SHORT_SHA}"
47+
fi
48+
mkdir -p "$OUTDIR/$SUBPATH"
49+
# Copy report as index.html for direct open
50+
if [ -f codestate_pr_report.html ]; then
51+
cp codestate_pr_report.html "$OUTDIR/$SUBPATH/index.html"
52+
else
53+
echo "<html><body><h3>No report found</h3></body></html>" > "$OUTDIR/$SUBPATH/index.html"
54+
fi
55+
# Copy JSON for programmatic access
56+
if [ -f codestate_pr_report.json ]; then
57+
cp codestate_pr_report.json "$OUTDIR/$SUBPATH/codestate_pr_report.json"
58+
fi
59+
# Minimal landing index pointing to this run's page
60+
cat > "$OUTDIR/index.html" << 'HTML'
61+
<!doctype html>
62+
<html lang="en"><head><meta charset="utf-8" />
63+
<meta name="viewport" content="width=device-width, initial-scale=1" />
64+
<title>CodeState Reports</title>
65+
<style>body{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif;padding:24px;line-height:1.5}a{color:#2563eb}</style>
66+
</head><body>
67+
<h2>CodeState PR Reports</h2>
68+
<p>This site hosts the latest generated PR report for this run.</p>
69+
<p><a id="runLink" href="SUBPATH_PLACEHOLDER/">Open this run's report</a></p>
70+
<p>Looking for older runs? Use the URL pattern: <code>/reports/pr-&lt;number&gt;/sha-&lt;short&gt;/</code></p>
71+
</body></html>
72+
HTML
73+
# Inject subpath
74+
sed -i "s|SUBPATH_PLACEHOLDER|$SUBPATH|g" "$OUTDIR/index.html"
75+
76+
- name: Publish to gh-pages
77+
uses: peaceiris/actions-gh-pages@v3
78+
with:
79+
github_token: ${{ secrets.GITHUB_TOKEN }}
80+
publish_branch: gh-pages
81+
publish_dir: ./site
82+
keep_files: true
83+
3384
- name: Upload HTML artifact
3485
uses: actions/upload-artifact@v4
3586
with:

tools/codestate_pr_report.py

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
Outputs:
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
1011
Env (recommended):
1112
- BASE_SHA: base commit SHA (e.g., github.event.pull_request.base.sha)
@@ -15,9 +16,8 @@
1516

1617
import os
1718
import re
18-
import sys
1919
import json
20-
import html
20+
import html as html_lib
2121
import shlex
2222
import subprocess
2323
from pathlib import Path
@@ -40,15 +40,13 @@ def run(cmd: str) -> Tuple[int, str, str]:
4040

4141

4242
def 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()}\nFalling 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\nNo 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

Comments
 (0)