Skip to content

Commit 304796a

Browse files
Adding markdown feedback of the report and improvements of visual feedback overall.
1 parent ce98d94 commit 304796a

6 files changed

Lines changed: 228 additions & 3 deletions

File tree

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ RUN apt-get update && \
88
# Install the RsMetaCheck tool from PyPI
99
RUN pip install --no-cache-dir rsmetacheck
1010

11-
# Copies the entrypoint script into the container
11+
# Copies scripts into the container
1212
COPY entrypoint.sh /entrypoint.sh
13+
COPY postprocess.py /postprocess.py
1314
RUN chmod +x /entrypoint.sh
1415

1516
# Code file to execute when the docker container starts up

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,25 @@ jobs:
4545
| `branch` | Branch of the repository to analyze. | No | |
4646
| `generate_codemeta` | Generate codemeta files for each repository. | No | `false` |
4747
| `verbose` | Include both detected AND undetected pitfalls in the output JSON-LD. | No | `false` |
48+
49+
### Outputs
50+
51+
| Output | Description |
52+
| ----------------- | -------------------------------------------------------------------- |
53+
| `has_pitfalls` | `'true'` if any pitfalls or warnings were detected |
54+
| `total_pitfalls` | Total number of pitfalls detected (P-codes) |
55+
| `total_warnings` | Total number of warnings detected (W-codes) |
56+
| `pitfalls_found` | JSON array of detected pitfall codes (e.g. `'["P001","P003"]'`) |
57+
| `warnings_found` | JSON array of detected warning codes (e.g. `'["W001","W002"]'`) |
58+
59+
### Visual Output
60+
61+
This action automatically reports results in the GitHub Actions UI:
62+
63+
1. **Step Summary** — A rendered Markdown table appears at the bottom of the workflow run page, showing all detected pitfalls and warnings with descriptions, counts, and per-repository details.
64+
65+
2. **Annotations** — Detected pitfalls appear as `::error::` annotations (red markers) and warnings as `::warning::` annotations (yellow markers) at the top of the workflow run page. When triggered by a pull request, these also appear inline on the diff view.
66+
67+
3. **Exit status** — If any pitfalls or warnings are found, the action exits with code 1, causing the step to show as failed (red) in the workflow UI.
68+
69+
4. **JSON-LD reports** — Per-repository JSON-LD files remain on disk in the `pitfalls_output` directory. Add `actions/upload-artifact@v4` to your workflow to make them downloadable as build artifacts.
10.6 KB
Binary file not shown.

action.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ inputs:
4141
required: false
4242
default: "false"
4343

44+
outputs:
45+
has_pitfalls:
46+
description: "'true' if any pitfalls or warnings were detected"
47+
total_pitfalls:
48+
description: "Total number of pitfalls detected"
49+
total_warnings:
50+
description: "Total number of warnings detected"
51+
pitfalls_found:
52+
description: "JSON array of detected pitfall codes (e.g. '[\"P001\",\"P003\"]')"
53+
warnings_found:
54+
description: "JSON array of detected warning codes (e.g. '[\"W001\",\"W002\"]')"
55+
4456
runs:
4557
using: "docker"
4658
image: "Dockerfile"

entrypoint.sh

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,21 @@ fi
5050
echo "Executing RsMetaCheck command:"
5151
echo "$CMD"
5252

53-
# Evaluate the command so spaces inside quotes are respected properly
54-
eval "$CMD"
53+
RSMETA_EXIT=0
54+
eval "$CMD" || RSMETA_EXIT=$?
55+
56+
# Run post-processing for GitHub Actions output
57+
if [ -n "$GITHUB_STEP_SUMMARY" ] || [ -n "$GITHUB_OUTPUT" ]; then
58+
echo "Generating GitHub Actions output..."
59+
POST_EXIT=0
60+
python3 /postprocess.py || POST_EXIT=$?
61+
else
62+
POST_EXIT=0
63+
fi
64+
65+
# Use post-process exit code (1 = pitfalls found) if it applies,
66+
# otherwise fall back to the rsmetacheck exit code
67+
if [ "$POST_EXIT" -ne 0 ]; then
68+
exit "$POST_EXIT"
69+
fi
70+
exit "$RSMETA_EXIT"

postprocess.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#!/usr/bin/env python3
2+
"""Post-process RsMetaCheck outputs for GitHub Actions."""
3+
4+
import json
5+
import os
6+
import sys
7+
from pathlib import Path
8+
9+
10+
def get_env_path(env_var: str, default: str) -> Path:
11+
path_str = os.environ.get(env_var) or default
12+
return Path(path_str)
13+
14+
15+
def main() -> int:
16+
analysis_path = get_env_path("INPUT_ANALYSIS_OUTPUT", "./analysis_results.json")
17+
pitfalls_dir = get_env_path("INPUT_PITFALLS_OUTPUT", "./pitfalls_outputs")
18+
step_summary_path = os.environ.get("GITHUB_STEP_SUMMARY")
19+
github_output_path = os.environ.get("GITHUB_OUTPUT")
20+
21+
if not analysis_path.exists():
22+
print(f"::warning::Analysis results not found at {analysis_path}")
23+
return 0
24+
if not pitfalls_dir.is_dir():
25+
print(f"::warning::Pitfalls output directory not found at {pitfalls_dir}")
26+
return 0
27+
28+
with open(analysis_path) as f:
29+
data = json.load(f)
30+
31+
summary = data.get("summary", {})
32+
total_repos = summary.get("total_repositories_analyzed", 0)
33+
total_pitfalls = summary.get("total_pitfalls_detected", 0)
34+
total_warnings = summary.get("total_warnings_detected", 0)
35+
repo_map = summary.get("evaluated_repositories", {})
36+
pitfall_entries = data.get("pitfalls & warnings", [])
37+
38+
pitfalls_found = []
39+
warnings_found = []
40+
for entry in pitfall_entries:
41+
code = entry.get("pitfall_code", "")
42+
count = entry.get("count", 0)
43+
if count == 0:
44+
continue
45+
if code.startswith("P"):
46+
pitfalls_found.append(entry)
47+
elif code.startswith("W"):
48+
warnings_found.append(entry)
49+
50+
per_repo_checks = {}
51+
for jsonld_file in sorted(pitfalls_dir.glob("*_pitfalls.jsonld")):
52+
with open(jsonld_file) as f:
53+
doc = json.load(f)
54+
software = doc.get("assessedSoftware", {})
55+
repo_name = software.get("name", jsonld_file.stem)
56+
checks = doc.get("checks", [])
57+
detected = []
58+
for check in checks:
59+
if check.get("output") == "true":
60+
indicator = check.get("assessesIndicator", {})
61+
code_url = indicator.get("@id", "")
62+
code = code_url.rstrip("/").rsplit("#", 1)[-1] if "#" in code_url else ""
63+
detected.append({
64+
"code": code,
65+
"evidence": check.get("evidence", ""),
66+
"suggestion": check.get("suggestion", ""),
67+
})
68+
if detected:
69+
per_repo_checks[repo_name] = {
70+
"url": software.get("url", ""),
71+
"commit": software.get("commit_id", ""),
72+
"detected": detected,
73+
}
74+
75+
if step_summary_path:
76+
_write_step_summary(
77+
step_summary_path, total_repos, total_pitfalls, total_warnings,
78+
repo_map, pitfalls_found, warnings_found, per_repo_checks,
79+
)
80+
81+
if github_output_path:
82+
_set_github_output(github_output_path, total_pitfalls, total_warnings, pitfalls_found, warnings_found)
83+
84+
for p in pitfalls_found:
85+
print(f"::error title={p.get('pitfall_code','')}::{p.get('pitfall_desc','')} (detected in {p.get('count',0)} repo(s))")
86+
for w in warnings_found:
87+
print(f"::warning title={w.get('pitfall_code','')}::{w.get('pitfall_desc','')} (detected in {w.get('count',0)} repo(s))")
88+
89+
return 1 if pitfalls_found else 0
90+
91+
92+
def _write_step_summary(path, total_repos, total_pitfalls, total_warnings,
93+
repo_map, pitfalls_found, warnings_found, per_repo_checks):
94+
with open(path, "a") as f:
95+
f.write("## RsMetaCheck Results\n\n")
96+
97+
if total_pitfalls > 0:
98+
status_icon = "❌"
99+
status_text = "Pitfalls Detected"
100+
elif total_warnings > 0:
101+
status_icon = "⚠️"
102+
status_text = "Warnings Detected"
103+
else:
104+
status_icon = "✅"
105+
status_text = "Passed"
106+
107+
f.write(f"| Status | Repositories | Pitfalls | Warnings |\n")
108+
f.write(f"|--------|-------------|----------|----------|\n")
109+
f.write(f"| {status_icon} **{status_text}** | {total_repos} | {total_pitfalls} | {total_warnings} |\n\n")
110+
111+
if repo_map:
112+
f.write("### Repositories Analyzed\n\n")
113+
f.write("| Repository | URL |\n")
114+
f.write("|------------|-----|\n")
115+
for name, info in repo_map.items():
116+
url = info.get("url", "")
117+
f.write(f"| {name} | [{url}]({url}) |\n")
118+
f.write("\n")
119+
120+
if pitfalls_found:
121+
f.write("### Detected Pitfalls\n\n")
122+
f.write("| Code | Description | Repos Affected | Rate |\n")
123+
f.write("|------|-------------|----------------|------|\n")
124+
for p in pitfalls_found:
125+
code = p.get("pitfall_code", "")
126+
desc = p.get("pitfall_desc", "")
127+
count = p.get("count", 0)
128+
pct = p.get("percentage", 0)
129+
f.write(f"| {code} | {desc} | {count} | {pct}% |\n")
130+
f.write("\n")
131+
132+
if warnings_found:
133+
f.write("### Detected Warnings\n\n")
134+
f.write("| Code | Description | Repos Affected | Rate |\n")
135+
f.write("|------|-------------|----------------|------|\n")
136+
for w in warnings_found:
137+
code = w.get("pitfall_code", "")
138+
desc = w.get("pitfall_desc", "")
139+
count = w.get("count", 0)
140+
pct = w.get("percentage", 0)
141+
f.write(f"| {code} | {desc} | {count} | {pct}% |\n")
142+
f.write("\n")
143+
144+
if per_repo_checks:
145+
f.write("### Per-Repository Details\n\n")
146+
for repo_name, info in per_repo_checks.items():
147+
repo_url = info.get("url", "")
148+
commit = info.get("commit", "")
149+
display_name = f"[{repo_name}]({repo_url})" if repo_url else repo_name
150+
f.write(f"<details>\n")
151+
f.write(f"<summary>{display_name}{len(info['detected'])} issue(s)</summary>\n\n")
152+
f.write("| Code | Evidence | Suggestion |\n")
153+
f.write("|------|----------|------------|\n")
154+
for d in info["detected"]:
155+
evidence = d.get("evidence", "").replace("\n", " ").replace("|", "\\|")
156+
suggestion = d.get("suggestion", "").replace("\n", " ").replace("|", "\\|")
157+
f.write(f"| {d['code']} | {evidence} | {suggestion} |\n")
158+
f.write(f"\n</details>\n\n")
159+
160+
if not pitfalls_found and not warnings_found:
161+
f.write("No pitfalls or warnings detected. ✅\n\n")
162+
163+
164+
def _set_github_output(path, total_pitfalls, total_warnings, pitfalls_found, warnings_found):
165+
with open(path, "a") as f:
166+
f.write(f"has_pitfalls={'true' if (total_pitfalls > 0 or total_warnings > 0) else 'false'}\n")
167+
f.write(f"total_pitfalls={total_pitfalls}\n")
168+
f.write(f"total_warnings={total_warnings}\n")
169+
f.write(f"pitfalls_found={json.dumps([p.get('pitfall_code','') for p in pitfalls_found])}\n")
170+
f.write(f"warnings_found={json.dumps([w.get('pitfall_code','') for w in warnings_found])}\n")
171+
172+
173+
if __name__ == "__main__":
174+
sys.exit(main())

0 commit comments

Comments
 (0)