Skip to content

Commit 0b12ce3

Browse files
committed
ci: split PR validation comments
1 parent e778f31 commit 0b12ce3

1 file changed

Lines changed: 96 additions & 40 deletions

File tree

.github/workflows/techapi-pr-validation-comment.yml

Lines changed: 96 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ jobs:
106106
import hashlib
107107
import json
108108
import re
109-
from collections import Counter, defaultdict
109+
from collections import Counter
110110
from pathlib import Path
111111
from typing import Any
112112
@@ -193,11 +193,11 @@ jobs:
193193
warnings.append("boost clock below base clock")
194194
return [f"{category}: {rel}: {warning}" for warning in warnings]
195195
196-
lines: list[str] = []
197-
lines.append("## Data summary")
198-
lines.append("")
199-
lines.append("| Category | Total | Verified | Unverified | Missing verified | Verified % |")
200-
lines.append("| --- | ---: | ---: | ---: | ---: | ---: |")
196+
stats_lines: list[str] = []
197+
stats_lines.append("## Data summary")
198+
stats_lines.append("")
199+
stats_lines.append("| Category | Total | Verified | Unverified | Missing verified | Verified % |")
200+
stats_lines.append("| --- | ---: | ---: | ---: | ---: | ---: |")
201201
202202
total_all = verified_all = unverified_all = missing_verified_all = 0
203203
by_category: dict[str, dict[str, int]] = {}
@@ -225,23 +225,24 @@ jobs:
225225
verified_all += verified
226226
unverified_all += unverified
227227
missing_verified_all += missing_verified
228-
lines.append(
228+
stats_lines.append(
229229
f"| {category} | {total} | {verified} | {unverified} | {missing_verified} | {pct} |"
230230
)
231231
tracked_all = verified_all + unverified_all
232232
pct_all = f"{(verified_all / tracked_all * 100):.1f}%" if tracked_all else "n/a"
233-
lines.append(
233+
stats_lines.append(
234234
f"| **all** | **{total_all}** | **{verified_all}** | **{unverified_all}** | "
235235
f"**{missing_verified_all}** | **{pct_all}** |"
236236
)
237237
238-
lines.append("")
239-
lines.append("## PR data delta")
240-
lines.append("")
241-
lines.append("| Category | Added | Modified | Deleted | Added verified | Added unverified | Added Kaggle-sourced |")
242-
lines.append("| --- | ---: | ---: | ---: | ---: | ---: | ---: |")
238+
change_lines: list[str] = []
239+
change_lines.append("## Changed data")
240+
change_lines.append("")
241+
change_lines.append("| Category | Added | Modified | Deleted | Added verified | Added unverified | Added Kaggle-sourced |")
242+
change_lines.append("| --- | ---: | ---: | ---: | ---: | ---: | ---: |")
243243
244244
all_added: list[tuple[str, str, Path]] = []
245+
changed_by_category: dict[str, dict[str, list[str]]] = {}
245246
for category in CATEGORIES:
246247
head = rel_jsons(HEAD, category)
247248
base = rel_jsons(BASE, category)
@@ -250,6 +251,11 @@ jobs:
250251
modified_keys = sorted(
251252
key for key in set(head) & set(base) if digest(head[key]) != digest(base[key])
252253
)
254+
changed_by_category[category] = {
255+
"added": added_keys,
256+
"modified": modified_keys,
257+
"deleted": deleted_keys,
258+
}
253259
added_verified = added_unverified = added_kaggle = 0
254260
for key in added_keys:
255261
record = load_json(head[key])
@@ -260,14 +266,43 @@ jobs:
260266
added_unverified += 1
261267
if has_kaggle_source(record):
262268
added_kaggle += 1
263-
lines.append(
269+
change_lines.append(
264270
f"| {category} | {len(added_keys)} | {len(modified_keys)} | {len(deleted_keys)} | "
265271
f"{added_verified} | {added_unverified} | {added_kaggle} |"
266272
)
267273
268-
lines.append("")
269-
lines.append("## Heuristic review")
270-
lines.append("")
274+
def display_record(root: Path, rel: str) -> str:
275+
record = load_json(root / rel)
276+
name = record.get("name")
277+
label = name if isinstance(name, str) and name else rel
278+
return f"`{rel}` - {label}"
279+
280+
def append_examples(title: str, root: Path, keys: list[str], limit: int = 15) -> None:
281+
if not keys:
282+
return
283+
change_lines.append("")
284+
change_lines.append(f"### {title}")
285+
for rel in keys[:limit]:
286+
change_lines.append(f"- {display_record(root, rel)}")
287+
if len(keys) > limit:
288+
change_lines.append(f"- ... {len(keys) - limit} more")
289+
290+
change_lines.append("")
291+
change_lines.append("## Changed record examples")
292+
for category, changes in changed_by_category.items():
293+
append_examples(f"{category} added", HEAD, changes["added"])
294+
append_examples(f"{category} modified", HEAD, changes["modified"])
295+
append_examples(f"{category} deleted", BASE, changes["deleted"])
296+
if not any(
297+
changes["added"] or changes["modified"] or changes["deleted"]
298+
for changes in changed_by_category.values()
299+
):
300+
change_lines.append("")
301+
change_lines.append("- No data file changes detected.")
302+
303+
change_lines.append("")
304+
change_lines.append("## Heuristic review")
305+
change_lines.append("")
271306
warnings: list[str] = []
272307
manufacturer_counter: Counter[str] = Counter()
273308
source_counter: Counter[str] = Counter()
@@ -285,20 +320,21 @@ jobs:
285320
286321
if manufacturer_counter:
287322
top = ", ".join(f"{name}: {count}" for name, count in manufacturer_counter.most_common(8))
288-
lines.append(f"- Added records by manufacturer/brand: {top}")
323+
change_lines.append(f"- Added records by manufacturer/brand: {top}")
289324
if source_counter:
290325
top = ", ".join(f"{name}: {count}" for name, count in source_counter.most_common())
291-
lines.append(f"- Added records by source class: {top}")
326+
change_lines.append(f"- Added records by source class: {top}")
292327
293328
if warnings:
294-
lines.append(f"- Heuristic warnings: {len(warnings)} total; showing first {min(MAX_WARNINGS, len(warnings))}.")
295-
lines.append("")
329+
change_lines.append(f"- Heuristic warnings: {len(warnings)} total; showing first {min(MAX_WARNINGS, len(warnings))}.")
330+
change_lines.append("")
296331
for warning in warnings[:MAX_WARNINGS]:
297-
lines.append(f" - {warning}")
332+
change_lines.append(f" - {warning}")
298333
else:
299-
lines.append("- Heuristic warnings: none found.")
334+
change_lines.append("- Heuristic warnings: none found.")
300335
301-
Path("quality-summary.md").write_text("\n".join(lines) + "\n", encoding="utf-8")
336+
Path("change-review.md").write_text("\n".join(change_lines) + "\n", encoding="utf-8")
337+
Path("data-stats.md").write_text("\n".join(stats_lines) + "\n", encoding="utf-8")
302338
PY
303339
304340
- name: Build PR comment
@@ -381,7 +417,7 @@ jobs:
381417
382418
{
383419
echo "<!-- techengine-pr-validation -->"
384-
echo "## TechEngine validation: ${result}"
420+
echo "## TechEngine change review: ${result}"
385421
echo
386422
echo "- PR: #${TECHAPI_PR_NUMBER}"
387423
echo "- Ref: \`${TECHAPI_HEAD_REF:-detached}\`"
@@ -394,10 +430,22 @@ jobs:
394430
echo "| \`python -m app.validate\` | $([ "${{ steps.validate.outputs.app_status }}" = "0" ] && echo PASS || echo FAIL) |"
395431
echo "| \`python integrity_check.py TechAPI/data --strict\` | $([ "${{ steps.validate.outputs.integrity_status }}" = "0" ] && echo PASS || echo FAIL) |"
396432
echo
397-
cat quality-summary.md
433+
cat change-review.md
434+
} > change-comment.md
435+
436+
{
437+
echo "<!-- techengine-pr-validation-stats -->"
438+
echo "## TechEngine validation stats: ${result}"
439+
echo
440+
echo "- PR: #${TECHAPI_PR_NUMBER}"
441+
echo "- Ref: \`${TECHAPI_HEAD_REF:-detached}\`"
442+
echo "- Commit: \`${short_sha}\`"
443+
echo "- Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
444+
echo
445+
cat data-stats.md
398446
echo
399447
cat validation-notes.md
400-
} > comment.md
448+
} > stats-comment.md
401449
402450
- name: Comment on TechAPI PR
403451
if: env.TECHAPI_COMMENT_TOKEN != ''
@@ -406,19 +454,27 @@ jobs:
406454
shell: bash
407455
run: |
408456
set -euo pipefail
409-
marker="<!-- techengine-pr-validation -->"
410-
comment_id="$(gh api "repos/GetTechAPI/TechAPI/issues/${TECHAPI_PR_NUMBER}/comments" --paginate \
411-
--jq ".[] | select(.body | contains(\"${marker}\")) | .id" | tail -n 1)"
412-
jq -n --rawfile body comment.md '{body: $body}' > comment.json
413-
if [ -n "$comment_id" ]; then
414-
gh api "repos/GetTechAPI/TechAPI/issues/comments/${comment_id}" \
415-
--method PATCH \
416-
--input comment.json
417-
else
418-
gh api "repos/GetTechAPI/TechAPI/issues/${TECHAPI_PR_NUMBER}/comments" \
419-
--method POST \
420-
--input comment.json
421-
fi
457+
upsert_comment() {
458+
local marker="$1"
459+
local body_file="$2"
460+
local payload_file="$3"
461+
local comment_id
462+
comment_id="$(gh api "repos/GetTechAPI/TechAPI/issues/${TECHAPI_PR_NUMBER}/comments" --paginate \
463+
--jq ".[] | select(.body | contains(\"${marker}\")) | .id" | tail -n 1)"
464+
jq -n --rawfile body "$body_file" '{body: $body}' > "$payload_file"
465+
if [ -n "$comment_id" ]; then
466+
gh api "repos/GetTechAPI/TechAPI/issues/comments/${comment_id}" \
467+
--method PATCH \
468+
--input "$payload_file"
469+
else
470+
gh api "repos/GetTechAPI/TechAPI/issues/${TECHAPI_PR_NUMBER}/comments" \
471+
--method POST \
472+
--input "$payload_file"
473+
fi
474+
}
475+
476+
upsert_comment "<!-- techengine-pr-validation -->" change-comment.md change-comment.json
477+
upsert_comment "<!-- techengine-pr-validation-stats -->" stats-comment.md stats-comment.json
422478
423479
- name: Warn when comment token is unset
424480
if: env.TECHAPI_COMMENT_TOKEN == ''

0 commit comments

Comments
 (0)