diff --git a/SyncPRSummary/action.yml b/SyncPRSummary/action.yml index e0e272e..bed68b1 100644 --- a/SyncPRSummary/action.yml +++ b/SyncPRSummary/action.yml @@ -1,13 +1,13 @@ name: Sync PR Summary -description: Update the managed PR summary block from workflow outputs and bot comments. +description: Consolidate workflow outputs and bot comments into a single managed bot comment on the PR. inputs: docs-content: - description: Documentation preview content to inject into the PR summary. + description: Documentation preview content to inject into the managed bot comment. required: false default: '' perf-content: - description: Performance summary content to inject into the PR summary. + description: Performance summary content to inject into the managed bot comment. required: false default: '' @@ -22,108 +22,87 @@ runs: github-token: ${{ github.token }} script: | const START_MARKER = ""; - const END_MARKER = ""; + const END_MARKER = ""; - const sectionsConfig = [ + const SECTIONS = [ { - title: "Documentation Preview", - fallback: "_Pending. No docs preview comment found yet._", - input: process.env.INPUT_DOCS_CONTENT, + key: "docs", + title: "Documentation Preview", + input: (process.env.INPUT_DOCS_CONTENT ?? "").trim(), + fallback: "_Pending. No docs preview available yet._", }, { - marker: "", - title: "Performance", - fallback: "_Pending. No performance comment found yet._", - input: process.env.INPUT_PERF_CONTENT, + key: "perf", + marker: "", + title: "Performance", + input: (process.env.INPUT_PERF_CONTENT ?? "").trim(), + fallback: "_Pending. No performance results available yet._", }, ]; - function extractManagedBlock(body) { - const start = body.indexOf(START_MARKER); - const end = body.indexOf(END_MARKER); - if (start === -1 || end === -1 || end < start) return ""; - return body.slice(start + START_MARKER.length, end).trim(); + function readSection(body, key) { + const open = ``; + const close = ``; + const s = body.indexOf(open); + const e = body.indexOf(close); + if (s === -1 || e === -1 || e <= s) return null; + return body.slice(s + open.length, e).trim(); } - function extractManagedSections(body) { - const managedBlock = extractManagedBlock(body); - if (!managedBlock) return new Map(); - - const sections = new Map(); - const sectionRegex = /^### (.+)$/gm; - const matches = [...managedBlock.matchAll(sectionRegex)]; - for (let i = 0; i < matches.length; i += 1) { - const title = matches[i][1]; - const contentStart = matches[i].index + matches[i][0].length; - const contentEnd = i + 1 < matches.length ? matches[i + 1].index : managedBlock.length; - const content = managedBlock.slice(contentStart, contentEnd).trim(); - if (content) sections.set(title, `### ${title}\n\n${content}`); - } - return sections; - } - - function replaceManagedBlock(body, managedBlock) { - const start = body.indexOf(START_MARKER); - const end = body.indexOf(END_MARKER); - - if (start === -1 && end === -1) { - if (!body) return managedBlock; - if (body.endsWith("\n\n")) return `${body}${managedBlock}`; - if (body.endsWith("\n")) return `${body}\n${managedBlock}`; - return `${body}\n\n${managedBlock}`; - } - if (start !== -1 && (end === -1 || end < start)) return `${body.slice(0, start)}${managedBlock}`; - if (start === -1 && end !== -1) return `${managedBlock}${body.slice(end + END_MARKER.length)}`; - return `${body.slice(0, start)}${managedBlock}${body.slice(end + END_MARKER.length)}`; + function buildBody(resolvedSections) { + const inner = resolvedSections + .map(({ key, title, content }) => + `\n### ${title}\n\n${content}\n`) + .join("\n\n"); + return `${START_MARKER}\n## CI Summary — GitHub Actions\n\n${inner}\n${END_MARKER}`; } + const { owner, repo } = context.repo; const prNumber = context.payload.pull_request.number; - const owner = context.repo.owner; - const repo = context.repo.repo; - const pr = await github.rest.pulls.get({ owner, repo, pull_number: prNumber }); + const allComments = await github.paginate(github.rest.issues.listComments, { + owner, repo, issue_number: prNumber, per_page: 100, + }); - const comments = await github.paginate(github.rest.issues.listComments, { - owner, - repo, - issue_number: prNumber, - per_page: 100, + const managedComment = allComments.find((c) => { + const user = c.user?.login; + const app = c.performed_via_github_app?.slug; + const isBot = user === "github-actions[bot]" || app === "github-actions"; + return isBot && (c.body || "").includes(START_MARKER); }); - const currentBody = pr.data.body || ""; - const existingSections = extractManagedSections(currentBody); - const latestByMarker = new Map(); + const managedBody = managedComment?.body ?? ""; - for (const comment of comments) { + const latestByMarker = new Map(); + for (const comment of allComments) { const body = comment.body || ""; const user = comment.user?.login; - const app = comment.performed_via_github_app?.slug; - if (user !== "github-actions[bot]" && app !== "github-actions") continue; - for (const { marker, title } of sectionsConfig) { + const app = comment.performed_via_github_app?.slug; + const isBot = user === "github-actions[bot]" || app === "github-actions"; + if (!isBot) continue; + + for (const { marker, title } of SECTIONS) { if (!marker) continue; if (!body.startsWith(marker)) continue; const content = body.slice(marker.length).trim(); - if (content) latestByMarker.set(marker, `### ${title}\n\n${content}`); + if (content) latestByMarker.set(marker, content); } } - const sections = sectionsConfig.map(({ marker, title, fallback, input }) => { - if (input) return `### ${title}\n\n${input.trim()}`; - return latestByMarker.get(marker) || existingSections.get(title) || `### ${title}\n\n${fallback}`; - }); + const resolvedSections = SECTIONS.map(({ key, marker, title, input, fallback }) => ({ + key, + title, + content: input || latestByMarker.get(marker) || readSection(managedBody, key) || fallback, + })); - const managedBlock = [ - START_MARKER, - "## CI Summary — GitHub Actions", - "", - ...sections, - END_MARKER, - ].join("\n\n"); + const newBody = buildBody(resolvedSections); - const updatedBody = replaceManagedBlock(currentBody, managedBlock); - if (updatedBody === currentBody) { - core.info("PR summary already up to date."); - return; + if (managedComment) { + if (managedComment.body !== newBody) { + await github.rest.issues.updateComment({ owner, repo, comment_id: managedComment.id, body: newBody }); + } else { + core.info("PR summary already up to date."); + } + } else { + await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body: newBody }); } - - await github.rest.pulls.update({ owner, repo, pull_number: prNumber, body: updatedBody });