feat(recipe-report): link recipe names in the lead to their mirror repos; 3-para concise lead

render() auto-links whole-word recipe mentions in the editorial lead to
git.autonomic.zone/recipe-maintainers/<recipe> (single regex pass, longest-name-first,
no href corruption). Skill: lead is ~3 short paragraphs (~150-180 words) incl. an
'anything strange worth looking into' paragraph. example-spec.json lead updated to the
concise target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
autonomic-bot
2026-06-04 02:17:19 +00:00
parent ea2d8c8210
commit a6efcec720
3 changed files with 27 additions and 139 deletions

View File

@ -29,11 +29,14 @@ Helper: `python3 /srv/cc-ci/cc-ci-plan/recipe-report.py {survey|render|publish}`
of the version bumps) for upgrades that fix **CVEs / security issues**. Anything **critical/high**
leads the page → the `security` bulletin (recipe · CVE id(s) + severity · what it fixes · PR link).
This is the most important section; be specific about severity and what's exposed if not merged.
- **Lead / editorial.** Write the `lead`: the **overall state of the recipe fleet** this week (how
healthy, what moved, any worrying trend) and **specific, opinionated suggestions of what to focus
on** — opus's voice, useful and concrete. **Keep it TIGHT: 2 short paragraphs, ~120 words total**
lead with the single most important thing, then security/focus in a sentence or two. (Trim hard;
the rest of the page carries the detail.)
- **Lead / editorial.** Write the `lead` in opus's voice — useful, concrete, opinionated. **~3 short
paragraphs, ~150180 words:**
1. **Fleet state** in a line or two — how healthy, what moved, any trend.
2. **What to focus on** — security/critical merges first, then the key failures.
3. **Anything strange worth looking into** — odd or unexpected failures, parser snags, PR-state
oddities (e.g. a recipe carrying two open PRs to reconcile), drift from the summary, leftover
artifacts. This is the "editor's eye" paragraph; flag what a careful maintainer would want to notice.
Lead with the single most important thing; the rest of the page carries the detail.
- **Needs attention** — GREEN PRs ready to merge + errors/failures to investigate (RED `!testme`,
recipe bugs). Short, specific prose + links. Flag cross-cutting issues (e.g. two open PRs to reconcile).
- **Routine** — minor/clean bumps, stale-test PRs (need operator `--with-tests`), up-to-date / skipped.

File diff suppressed because one or more lines are too long

View File

@ -21,7 +21,7 @@ SPEC SHAPE (the agent writes this JSON):
"pr":"#4","pr_url":"","notes":""}]}
PUBLIC PAGE — include only public-safe data (no secrets/tokens/raw logs).
"""
import base64, html, json, os, subprocess, sys, urllib.request
import base64, html, json, os, re, subprocess, sys, urllib.request
from datetime import datetime, timezone
LOGDIR = "/srv/cc-ci/.cc-ci-logs"
@ -121,6 +121,17 @@ def _esc(s):
return html.escape(str(s or ""))
def _linkify_recipes(text, repo_url):
"""Linkify whole-word recipe-name mentions to their mirror repo. ONE pass over the (already
HTML-escaped) text, longest names first so 'custom-html-tiny' wins over 'custom-html'; re.sub does
not re-scan inserted hrefs, so URLs that end in a recipe name aren't double-linked."""
if not repo_url:
return text
names = sorted(repo_url, key=len, reverse=True)
pat = re.compile(r"(?<![\w-])(" + "|".join(re.escape(n) for n in names) + r")(?![\w-])")
return pat.sub(lambda m: f'<a href="{repo_url[m.group(1)]}">{m.group(1)}</a>', text)
def _links(links):
if not links:
return ""
@ -172,8 +183,13 @@ def render(spec_path, out_path):
gen = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
sub = s.get("subtitle", "Week of " + s["date"])
lead = s.get("lead", "") or ""
# Auto-link recipe-name mentions in the lead to their mirror repos.
gitea = _env().get("GITEA_URL", "git.autonomic.zone")
repo_url = {r["recipe"]: f"https://{gitea}/recipe-maintainers/{r['recipe']}"
for r in (s.get("table") or []) if r.get("recipe")}
if lead and "<p>" not in lead:
lead = "".join(f"<p>{_esc(p.strip())}</p>" for p in lead.split("\n\n") if p.strip())
lead = "".join(f"<p>{_linkify_recipes(_esc(p.strip()), repo_url)}</p>"
for p in lead.split("\n\n") if p.strip())
body = (_mast() +
f'<div class="dateline"><span>{_esc(sub)}</span>'
f'<span>report.ci.commoninternet.net</span><span>{gen}</span></div>'