Files
2026-07-16 13:20:13 -07:00

130 lines
4.2 KiB
JavaScript

/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
/**
* @param {{ github: any, context: any, core: any, reportFile: string, prNumber: number, reportUrl?: string }} params
*/
async function postReportComment({ github, context, core, reportFile, prNumber, reportUrl }) {
if (!prNumber) {
core.info('No PR number provided, skipping GHA comment.');
return;
}
const report = fs.readFileSync(path.resolve(process.cwd(), reportFile), 'utf8');
core.info(`Posting comment to PR #${prNumber}`);
const prNodeId = await collapsePreviousComments(github, context, prNumber, magicComment(context));
if (!prNodeId) {
core.warning(`No PR node ID found for #${prNumber}, skipping GHA comment.`);
return;
}
const url = await addNewReportComment(github, context, prNodeId, report, reportUrl);
core.info(`Posted comment: ${url}`);
}
function workflowRunName(context) {
// When used via 'workflow_run' event.
const name = context.payload.workflow_run?.name;
if (name)
return name;
// When used via 'pull_request'/'push' event.
return process.env.GITHUB_WORKFLOW;
}
function magicComment(context) {
return `<!-- Generated by Playwright markdown reporter for ${workflowRunName(context)} in job ${process.env.GITHUB_JOB} -->`;
}
async function collapsePreviousComments(github, context, prNumber, sentinel) {
const { owner, repo } = context.repo;
const data = await github.graphql(`
query($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
id
comments(last: 100) {
nodes {
id
body
author {
__typename
login
}
}
}
}
}
}
`, { owner, repo, prNumber });
const comments = data.repository.pullRequest?.comments.nodes?.filter(comment =>
comment?.author?.__typename === 'Bot' &&
comment?.author?.login === 'github-actions' &&
comment.body?.includes(sentinel));
const prId = data.repository.pullRequest?.id;
if (!comments?.length)
return prId;
const variableDecls = comments.map((_, i) => `$id${i}: ID!`).join(', ');
const mutations = comments.map((_, i) =>
`m${i}: minimizeComment(input: { subjectId: $id${i}, classifier: OUTDATED }) { clientMutationId }`);
const subjectIds = Object.fromEntries(comments.map((comment, i) => [`id${i}`, comment.id]));
await github.graphql(`
mutation(${variableDecls}) {
${mutations.join('\n')}
}
`, subjectIds);
return prId;
}
async function addNewReportComment(github, context, prNodeId, report, reportUrl) {
const mergeWorkflowUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const body = formatComment([
magicComment(context),
`### ${reportUrl ? `[Test results](${reportUrl})` : 'Test results'} for "${workflowRunName(context)}"`,
report,
'',
'---',
'',
`Merge [workflow run](${mergeWorkflowUrl}).`
]);
const response = await github.graphql(`
mutation($subjectId: ID!, $body: String!) {
addComment(input: {subjectId: $subjectId, body: $body}) {
commentEdge {
node {
... on IssueComment {
url
}
}
}
}
}
`, { subjectId: prNodeId, body });
return response.addComment.commentEdge.node?.url;
}
function formatComment(lines) {
let body = lines.join('\n');
if (body.length > 65535)
body = body.substring(0, 65000) + `... ${body.length - 65000} more characters`;
return body;
}
module.exports = { collapsePreviousComments, postReportComment };