Skip to content

Commit 6ae64fe

Browse files
feat(action): render PR comment to file and expose path output (#38)
1 parent 13371ec commit 6ae64fe

2 files changed

Lines changed: 131 additions & 55 deletions

File tree

action.yml

Lines changed: 42 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ outputs:
5757
tests_output:
5858
description: Full output of the test runner
5959
value: ${{ steps.tests.outputs.output }}
60+
comment_path:
61+
description: >
62+
Path to a file containing the rendered Markdown PR comment body.
63+
Empty when no integration directories changed. Useful for callers
64+
that want to upload the comment as an artifact and post it from a
65+
separate workflow (e.g. workflow_run) — typically required for
66+
pull requests opened from forks, where the pull_request event
67+
runs with a read-only GITHUB_TOKEN.
68+
value: ${{ steps.render.outputs.path }}
6069

6170
runs:
6271
using: composite
@@ -176,6 +185,38 @@ runs:
176185
fi
177186
exit $EXIT_CODE
178187
188+
- name: Render comment markdown
189+
id: render
190+
if: always() && steps.detect.outputs.dirs != ''
191+
shell: bash
192+
env:
193+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
194+
SERVER_URL: ${{ github.server_url }}
195+
REPOSITORY: ${{ github.repository }}
196+
COMMIT_MSG: ${{ steps.detect.outputs.commit_msg }}
197+
UPDATED_AT: ${{ github.event.pull_request.updated_at }}
198+
DIRS: ${{ steps.detect.outputs.dirs }}
199+
STRUCTURE_OUTCOME: ${{ steps.structure.outcome }}
200+
STRUCTURE_WARN: ${{ steps.structure.outputs.has_warnings }}
201+
STRUCTURE_OUTPUT: ${{ steps.structure.outputs.output }}
202+
CODE_OUTCOME: ${{ steps.code.outcome }}
203+
CODE_WARN: ${{ steps.code.outputs.has_warnings }}
204+
CODE_OUTPUT: ${{ steps.code.outputs.output }}
205+
TESTS_OUTCOME: ${{ steps.tests.outcome }}
206+
TESTS_WARN: ${{ steps.tests.outputs.has_warnings }}
207+
TESTS_OUTPUT: ${{ steps.tests.outputs.output }}
208+
README_OUTCOME: ${{ steps.readme.outcome }}
209+
README_WARN: ${{ steps.readme.outputs.has_warnings }}
210+
README_OUTPUT: ${{ steps.readme.outputs.output }}
211+
VERSION_OUTCOME: ${{ steps.version.outcome }}
212+
VERSION_WARN: ${{ steps.version.outputs.has_warnings }}
213+
VERSION_OUTPUT: ${{ steps.version.outputs.output }}
214+
run: |
215+
set -euo pipefail
216+
OUT="${{ runner.temp }}/validation-comment.md"
217+
python "${{ github.action_path }}/scripts/render_comment.py" > "$OUT"
218+
echo "path=$OUT" >> "$GITHUB_OUTPUT"
219+
179220
- name: Delete stale PR comment
180221
if: always() && steps.detect.outputs.dirs == '' && inputs.post_comment == 'true'
181222
uses: marocchino/sticky-pull-request-comment@v2
@@ -188,61 +229,7 @@ runs:
188229
uses: marocchino/sticky-pull-request-comment@v2
189230
with:
190231
header: validation-results
191-
message: |
192-
## 🔍 Integration Validation Results
193-
194-
**Commit:** [`${{ github.event.pull_request.head.sha }}`](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.event.pull_request.head.sha }}) · ${{ steps.detect.outputs.commit_msg }}
195-
**Updated:** ${{ github.event.pull_request.updated_at }}
196-
197-
**Changed directories:** `${{ steps.detect.outputs.dirs }}`
198-
199-
| Check | Result |
200-
|-------|--------|
201-
| Structure | ${{ steps.structure.outcome == 'failure' && '❌ Failed' || (steps.structure.outputs.has_warnings == 'true' && '⚠️ Passed with warnings' || '✅ Passed') }} |
202-
| Code | ${{ steps.code.outcome == 'failure' && '❌ Failed' || (steps.code.outputs.has_warnings == 'true' && '⚠️ Passed with warnings' || '✅ Passed') }} |
203-
| Tests | ${{ steps.tests.outcome == 'failure' && '❌ Failed' || (steps.tests.outputs.has_warnings == 'true' && '⚠️ Passed with warnings' || '✅ Passed') }} |
204-
| README | ${{ steps.readme.outcome == 'failure' && '❌ Failed' || (steps.readme.outputs.has_warnings == 'true' && '⚠️ Passed with warnings' || '✅ Passed') }} |
205-
| Version | ${{ steps.version.outcome == 'failure' && '❌ Failed' || (steps.version.outputs.has_warnings == 'true' && '⚠️ Passed with warnings' || '✅ Passed') }} |
206-
207-
<details><summary>${{ steps.structure.outcome == 'failure' && '❌' || (steps.structure.outputs.has_warnings == 'true' && '⚠️' || '✅') }} Structure Check output</summary>
208-
209-
```
210-
${{ steps.structure.outputs.output }}
211-
```
212-
213-
</details>
214-
215-
<details><summary>${{ steps.code.outcome == 'failure' && '❌' || (steps.code.outputs.has_warnings == 'true' && '⚠️' || '✅') }} Code Check output</summary>
216-
217-
```
218-
${{ steps.code.outputs.output }}
219-
```
220-
221-
</details>
222-
223-
<details><summary>${{ steps.tests.outcome == 'failure' && '❌' || (steps.tests.outputs.has_warnings == 'true' && '⚠️' || '✅') }} Tests output</summary>
224-
225-
```
226-
${{ steps.tests.outputs.output }}
227-
```
228-
229-
</details>
230-
231-
<details><summary>${{ steps.readme.outcome == 'failure' && '❌' || (steps.readme.outputs.has_warnings == 'true' && '⚠️' || '✅') }} README Check output</summary>
232-
233-
```
234-
${{ steps.readme.outputs.output }}
235-
```
236-
237-
</details>
238-
239-
<details><summary>${{ steps.version.outcome == 'failure' && '❌' || (steps.version.outputs.has_warnings == 'true' && '⚠️' || '✅') }} Version Check output</summary>
240-
241-
```
242-
${{ steps.version.outputs.output }}
243-
```
244-
245-
</details>
232+
path: ${{ steps.render.outputs.path }}
246233

247234
- name: Fail if any check failed
248235
if: always() && (steps.structure.outcome == 'failure' || steps.code.outcome == 'failure' || steps.tests.outcome == 'failure' || steps.readme.outcome == 'failure' || steps.version.outcome == 'failure')

scripts/render_comment.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env python3
2+
"""Render the validation-results PR comment as Markdown to stdout.
3+
4+
All inputs are read from environment variables so that multi-line tool
5+
output (which can contain backticks, ``EOF`` markers, etc.) is handled
6+
correctly without any YAML or heredoc escaping.
7+
8+
Expected env vars per check (prefix is one of STRUCTURE / CODE / TESTS /
9+
README / VERSION):
10+
11+
{PREFIX}_OUTCOME one of "success", "failure", "skipped", ""
12+
{PREFIX}_WARN "true" if the check emitted warnings
13+
{PREFIX}_OUTPUT full captured stdout/stderr of the check
14+
15+
Plus context vars: HEAD_SHA, SERVER_URL, REPOSITORY, COMMIT_MSG,
16+
UPDATED_AT, DIRS.
17+
"""
18+
19+
from __future__ import annotations
20+
21+
import os
22+
import sys
23+
24+
25+
CHECKS: list[tuple[str, str]] = [
26+
("Structure", "STRUCTURE"),
27+
("Code", "CODE"),
28+
("Tests", "TESTS"),
29+
("README", "README"),
30+
("Version", "VERSION"),
31+
]
32+
33+
34+
def _status(outcome: str, has_warnings: str) -> tuple[str, str]:
35+
"""Return (icon, table_text) for a check result."""
36+
if outcome == "failure":
37+
return "❌", "❌ Failed"
38+
if has_warnings == "true":
39+
return "⚠️", "⚠️ Passed with warnings"
40+
return "✅", "✅ Passed"
41+
42+
43+
def _section(label: str, prefix: str) -> str:
44+
outcome = os.environ.get(f"{prefix}_OUTCOME", "")
45+
warn = os.environ.get(f"{prefix}_WARN", "")
46+
output = os.environ.get(f"{prefix}_OUTPUT", "") or "(no output)"
47+
icon, _ = _status(outcome, warn)
48+
return (
49+
f"<details><summary>{icon} {label} Check output</summary>\n\n"
50+
f"```\n{output}\n```\n\n"
51+
f"</details>\n"
52+
)
53+
54+
55+
def main() -> int:
56+
head_sha = os.environ.get("HEAD_SHA", "")
57+
server_url = os.environ.get("SERVER_URL", "")
58+
repository = os.environ.get("REPOSITORY", "")
59+
commit_msg = os.environ.get("COMMIT_MSG", "")
60+
updated_at = os.environ.get("UPDATED_AT", "")
61+
dirs = os.environ.get("DIRS", "")
62+
63+
rows = []
64+
for label, prefix in CHECKS:
65+
outcome = os.environ.get(f"{prefix}_OUTCOME", "")
66+
warn = os.environ.get(f"{prefix}_WARN", "")
67+
_, text = _status(outcome, warn)
68+
rows.append(f"| {label} | {text} |")
69+
70+
sections = [_section(label, prefix) for label, prefix in CHECKS]
71+
72+
md = (
73+
"## 🔍 Integration Validation Results\n\n"
74+
f"**Commit:** [`{head_sha}`]({server_url}/{repository}/commit/{head_sha})"
75+
f" · {commit_msg}\n"
76+
f"**Updated:** {updated_at}\n\n"
77+
f"**Changed directories:** `{dirs}`\n\n"
78+
"| Check | Result |\n"
79+
"|-------|--------|\n"
80+
+ "\n".join(rows)
81+
+ "\n\n"
82+
+ "\n".join(sections)
83+
)
84+
sys.stdout.write(md)
85+
return 0
86+
87+
88+
if __name__ == "__main__":
89+
raise SystemExit(main())

0 commit comments

Comments
 (0)