// Copyright (c) 2026 Lark Technologies Pte. Ltd. // SPDX-License-Identifier: MIT const SUMMARY_MARKER_PREFIX = "`; } function parseSummaryMarker(body) { const match = //.exec(String(body || "")); if (!match) { return {}; } const metadata = {}; for (const part of match[1].trim().split(/\s+/)) { const attr = /^([A-Za-z0-9_-]+)=([^ ]*)$/.exec(part); if (attr) { metadata[attr[1]] = attr[2]; } } return metadata; } function markerRunNumber(value) { const run = Number(String(value || "").trim()); return Number.isInteger(run) && run > 0 ? run : 0; } function summaryCommentRunNumber(comment) { return markerRunNumber(parseSummaryMarker(comment?.body).run); } function targetRunNumber(target) { return markerRunNumber(target?.runId); } function hasNewerSummaryComment(comments, target) { const currentRun = targetRunNumber(target); return qualitySummaryComments(comments) .some((comment) => summaryCommentRunNumber(comment) > currentRun); } function isBotComment(comment) { return !!(comment && comment.user && comment.user.type === "Bot"); } function hasQualitySummaryMarker(body) { const text = String(body || ""); return text.includes(SUMMARY_MARKER_PREFIX) || LEGACY_SUMMARY_MARKER_PREFIXES.some((prefix) => text.includes(prefix)); } function qualitySummaryComments(comments) { return (Array.isArray(comments) ? comments : []) .filter((comment) => isBotComment(comment) && hasQualitySummaryMarker(comment.body)); } function findQualitySummaryComment(comments) { return qualitySummaryComments(comments)[0] || null; } function finalSummaryBody(target, markdown) { return `${summaryMarker(target)}\n${String(markdown || "")}`.slice(0, 60000); } async function listIssueComments(github, context, pr) { return github.paginate(github.rest.issues.listComments, { owner: context.repo.owner, repo: context.repo.repo, issue_number: pr, per_page: 100, }); } async function publishQualitySummary({ github, context, pr, target, markdown, beforeWrite }) { const body = finalSummaryBody(target, markdown); const comments = await listIssueComments(github, context, pr); const summaries = qualitySummaryComments(comments); if (hasNewerSummaryComment(summaries, target)) { return { action: "skipped-newer-summary" }; } const existing = summaries[0] || null; if (beforeWrite && !(await beforeWrite(existing ? "update" : "creation"))) { return { action: "skipped" }; } for (const duplicate of summaries.slice(1)) { await github.rest.issues.deleteComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: duplicate.id, }); } if (existing) { await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: existing.id, body, }); return { action: "updated", commentId: existing.id, body }; } await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: pr, body, }); return { action: "created", body }; } async function deleteQualitySummaries({ github, context, pr, target, beforeWrite }) { const comments = await listIssueComments(github, context, pr); const existing = qualitySummaryComments(comments); if (hasNewerSummaryComment(existing, target)) { return { deleted: 0, skipped: true }; } if (existing.length > 0 && beforeWrite && !(await beforeWrite("delete"))) { return { deleted: 0, skipped: true }; } for (const comment of existing) { await github.rest.issues.deleteComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: comment.id, }); } return { deleted: existing.length }; } module.exports = { SUMMARY_MARKER_PREFIX, deleteQualitySummaries, finalSummaryBody, findQualitySummaryComment, hasQualitySummaryMarker, inlineCode, listIssueComments, markdownText, publishQualitySummary, qualitySummaryComments, sanitizeMarkdownBody, summaryMarker, };