diff --git a/sources/package-lock.json b/sources/package-lock.json index bfb30b0d..c06a431c 100644 --- a/sources/package-lock.json +++ b/sources/package-lock.json @@ -17,6 +17,7 @@ "@actions/glob": "0.6.1", "@actions/http-client": "4.0.0", "@actions/tool-cache": "4.0.0", + "@octokit/graphql-schema": "15.26.1", "@octokit/webhooks-types": "7.6.1", "cheerio": "1.2.0", "semver": "7.7.4", @@ -2403,6 +2404,16 @@ "node": ">= 18" } }, + "node_modules/@octokit/graphql-schema": { + "version": "15.26.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql-schema/-/graphql-schema-15.26.1.tgz", + "integrity": "sha512-RFDC2MpRBd4AxSRvUeBIVeBU7ojN/SxDfALUd7iVYOSeEK3gZaqR2MGOysj4Zh2xj2RY5fQAUT+Oqq7hWTraMA==", + "license": "MIT", + "dependencies": { + "graphql": "^16.0.0", + "graphql-tag": "^2.10.3" + } + }, "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { "version": "24.2.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", @@ -5590,6 +5601,30 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/graphql": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.1.tgz", + "integrity": "sha512-gGgrVCoDKlIZ8fIqXBBb0pPKqDgki0Z/FSKNiQzSGj2uEYHr1tq5wmBegGwJx6QB5S5cM0khSBpi/JFHMCvsmQ==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/handlebars": { "version": "4.7.9", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", diff --git a/sources/package.json b/sources/package.json index 4f21bc02..3453eb05 100644 --- a/sources/package.json +++ b/sources/package.json @@ -43,6 +43,7 @@ "@actions/glob": "0.6.1", "@actions/http-client": "4.0.0", "@actions/tool-cache": "4.0.0", + "@octokit/graphql-schema": "15.26.1", "@octokit/webhooks-types": "7.6.1", "cheerio": "1.2.0", "semver": "7.7.4", diff --git a/sources/src/configuration.ts b/sources/src/configuration.ts index 3e519e49..d698efbf 100644 --- a/sources/src/configuration.ts +++ b/sources/src/configuration.ts @@ -206,6 +206,10 @@ export class SummaryConfig { return this.shouldAddJobSummary(this.getJobSummaryOption(), hasFailure) } + canAddPRComment(): boolean { + return this.getPRCommentOption() !== JobSummaryOption.Never + } + shouldAddPRComment(hasFailure: boolean): boolean { return this.shouldAddJobSummary(this.getPRCommentOption(), hasFailure) } diff --git a/sources/src/job-summary.ts b/sources/src/job-summary.ts index 08d53ca7..2cde75c3 100644 --- a/sources/src/job-summary.ts +++ b/sources/src/job-summary.ts @@ -1,8 +1,9 @@ import * as core from '@actions/core' import * as github from '@actions/github' +import {Repository} from '@octokit/graphql-schema' import {BuildResult} from './build-results' -import {SummaryConfig, getActionId, getGithubToken} from './configuration' +import {DependencyGraphConfig, getActionId, getGithubToken, getJobMatrix, SummaryConfig} from './configuration' import {Deprecation, getDeprecations, getErrors} from './deprecation-collector' export async function generateJobSummary( @@ -33,6 +34,10 @@ export async function generateJobSummary( core.info('============================') } + if (config.canAddPRComment()) { + await minimizeObsoletePRComments() + } + if (config.shouldAddPRComment(hasFailure)) { await addPRComment(summaryTable) } @@ -48,7 +53,8 @@ async function addPRComment(jobSummary: string): Promise { const pull_request_number = context.payload.pull_request.number core.info(`Adding Job Summary as comment to PR #${pull_request_number}.`) - const prComment = `

Job Summary for Gradle

+ const prComment = `${jobMarker(context)} +

Job Summary for Gradle

${context.workflow} :: ${context.job}
@@ -57,6 +63,7 @@ ${jobSummary}` const github_token = getGithubToken() const octokit = github.getOctokit(github_token) + try { await octokit.rest.issues.createComment({ ...context.repo, @@ -201,3 +208,60 @@ function truncateString(str: string, maxLength: number): string { return str } } + +async function minimizeObsoletePRComments(): Promise { + const context = github.context + if (context.payload.pull_request == null) { + core.info('No pull_request trigger detected: not minimizing obsolete PR comments') + return + } + + const prNumber = context.payload.pull_request.number + core.info(`Minimizing obsolete Job Summary comments on PR #${prNumber}.`) + + const marker = jobMarker(context) + const octokit = github.getOctokit(getGithubToken()) + const {owner, repo} = context.repo + + const query = ` + query($owner: String!, $repo: String!, $prNumber: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $prNumber) { + comments(last: 100) { + nodes { id body isMinimized url } + } + } + } + } + ` + let comments + try { + const {repository} = await octokit.graphql<{repository: Repository}>(query, {owner, repo, prNumber}) + comments = repository.pullRequest?.comments?.nodes?.filter((c): c is NonNullable => c !== null) ?? [] + } catch (error) { + return core.warning(`Failed to fetch comments: ${error}`) + } + + const mutation = ` + mutation($id: ID!) { + minimizeComment(input: {subjectId: $id, classifier: OUTDATED}) { + clientMutationId + } + } + ` + + const commentsToMinimize = comments + .filter(c => !c.isMinimized && c.body.includes(marker)) + .map(async c => + octokit + .graphql(mutation, {id: c.id}) + .then(() => core.info(`Successfully minimized (id:${c.id}, url:${c.url})`)) + .catch(e => core.warning(`Failed to minimize (id:${c.id}, url:${c.url}, error:${e?.message || e})`)) + ) + await Promise.allSettled(commentsToMinimize) +} + +function jobMarker(context: typeof github.context): string { + const jobCorrelator = DependencyGraphConfig.constructJobCorrelator(context.workflow, context.job, getJobMatrix()) + return `` +}