|
| 1 | +/** |
| 2 | + * What a pull request changes in the licence facts of catalog entries, as a |
| 3 | + * Markdown table for the CI step summary (site/pr-diff.mjs). A reviewer sees |
| 4 | + * every changed licence field, old and new, beside the newest Evidence line |
| 5 | + * that should back it, instead of reading raw frontmatter diffs. Pure. |
| 6 | + */ |
| 7 | +import { evidenceSection } from "./frontmatter.mjs"; |
| 8 | + |
| 9 | +/** The frontmatter fields that carry a licence fact, in table order. */ |
| 10 | +export const LICENCE_FIELDS = [ |
| 11 | + "status", |
| 12 | + "license", |
| 13 | + "license_spdx", |
| 14 | + "commercial", |
| 15 | + "attribution_required", |
| 16 | + "attribution_string", |
| 17 | + "engine_lock", |
| 18 | + "preview", |
| 19 | + "verified", |
| 20 | +]; |
| 21 | + |
| 22 | +const DATE_RE = /\b(\d{4}-\d{2}-\d{2})\b/g; |
| 23 | + |
| 24 | +const text = (v) => (v === undefined || v === null || v === "" ? null : Array.isArray(v) ? v.join(", ") : String(v)); |
| 25 | + |
| 26 | +/** Changed licence fields between two frontmatter objects (either may be null: added or removed). */ |
| 27 | +export function licenceChanges(before, after) { |
| 28 | + const out = []; |
| 29 | + for (const field of LICENCE_FIELDS) { |
| 30 | + const a = before ? text(before[field]) : null; |
| 31 | + const b = after ? text(after[field]) : null; |
| 32 | + if (a !== b) out.push({ field, before: a, after: b }); |
| 33 | + } |
| 34 | + return out; |
| 35 | +} |
| 36 | + |
| 37 | +/** The Evidence line carrying the newest date (dates inside URLs ignored), or null. */ |
| 38 | +export function newestEvidenceLine(body) { |
| 39 | + const ev = evidenceSection(String(body || "")); |
| 40 | + if (!ev) return null; |
| 41 | + let best = null; |
| 42 | + for (const line of ev.split(/\r?\n/)) { |
| 43 | + const prose = line.replace(/\]\([^)]*\)/g, "]").replace(/https?:\/\/\S+/g, ""); |
| 44 | + for (const m of prose.matchAll(DATE_RE)) { |
| 45 | + if (!best || m[1] > best.date) best = { date: m[1], line: line.trim() }; |
| 46 | + } |
| 47 | + } |
| 48 | + return best ? best.line : null; |
| 49 | +} |
| 50 | + |
| 51 | +/** A value as an inert inline code span (or an em dash for none), safe inside a table cell. */ |
| 52 | +function cell(v, max = 160) { |
| 53 | + if (v === null || v === undefined) return "—"; |
| 54 | + let s = String(v).replace(/\s+/g, " ").trim(); |
| 55 | + if (s.length > max) s = `${s.slice(0, max - 1)}…`; |
| 56 | + s = s.replaceAll("`", "'").replaceAll("|", "\\|"); |
| 57 | + return `\`${s}\``; |
| 58 | +} |
| 59 | + |
| 60 | +/** |
| 61 | + * The step-summary Markdown for a list of `{ file, change: "added"|"removed"|"modified", |
| 62 | + * fields: [{ field, before, after }], evidence }`. |
| 63 | + */ |
| 64 | +export function licenceDiffMarkdown(rows) { |
| 65 | + const changed = rows.filter((r) => r.fields.length); |
| 66 | + const lines = ["## Licence fields changed in this pull request", ""]; |
| 67 | + if (!changed.length) { |
| 68 | + lines.push("No licence field changed in any catalog entry.", ""); |
| 69 | + return lines.join("\n"); |
| 70 | + } |
| 71 | + lines.push( |
| 72 | + `${changed.length} ${changed.length === 1 ? "entry" : "entries"} changed. Check each new value against the Evidence line beside it.`, |
| 73 | + "", |
| 74 | + "| Entry | Field | Before | After | Newest Evidence line |", |
| 75 | + "| --- | --- | --- | --- | --- |" |
| 76 | + ); |
| 77 | + for (const r of changed) { |
| 78 | + const label = `${cell(r.file)}${r.change === "modified" ? "" : ` (${r.change})`}`; |
| 79 | + r.fields.forEach((f, i) => { |
| 80 | + lines.push(`| ${i ? "" : label} | ${f.field} | ${cell(f.before)} | ${cell(f.after)} | ${i ? "" : cell(r.evidence, 220)} |`); |
| 81 | + }); |
| 82 | + } |
| 83 | + lines.push(""); |
| 84 | + return lines.join("\n"); |
| 85 | +} |
0 commit comments