mirror of
https://github.com/gradle/actions.git
synced 2026-07-26 14:14:32 +02:00
Hide obsolete Job summaries (#902)
- Injects a `<!-- gradle-job-summary: ${jobCorrelator} -->` marker on
each job summary
- Lists 100 last comments: unfortunately there is no API to specifically
filter for comments, and checking the last 100 comments (the limit) is
usually enough and does not require iterating over pages
- Mutate comments having this expected marker
I tried to add some tests, but I'm not familiar enough to setup a
complete test suite with proper mocking of GitHub/Octokit with jest.
I could potentially extract the `prComment` creation to check for the
marker presence, let me know.
Note: it seems like there is currently an issue on mutating comments as
`OUTDATED` through graphql. Although it does not work as expected
(flagging as OUTDATED) the comments are still minimized, which is what
we want.
- https://github.com/orgs/community/discussions/19865
Implements #176
---------
Co-authored-by: Daz DeBoer <daz@gradle.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Daz DeBoer
Claude Opus 4.8
parent
a740661292
commit
318eed7038
@@ -548,6 +548,11 @@ jobs:
|
|||||||
- run: ./gradlew build --scan
|
- run: ./gradlew build --scan
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When a comment is added, any earlier Job Summary comments posted by the same job on that Pull Request are
|
||||||
|
automatically minimized, so only the most recent result remains expanded. This also applies when
|
||||||
|
`add-job-summary-as-pr-comment: 'on-failure'` is used: once a later run of the job succeeds, the previous
|
||||||
|
failure comment is collapsed.
|
||||||
|
|
||||||
Note that to add a Pull Request comment, the workflow must be configured with the `pull-requests: write` permission.
|
Note that to add a Pull Request comment, the workflow must be configured with the `pull-requests: write` permission.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -206,6 +206,10 @@ export class SummaryConfig {
|
|||||||
return this.shouldAddJobSummary(this.getJobSummaryOption(), hasFailure)
|
return this.shouldAddJobSummary(this.getJobSummaryOption(), hasFailure)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canAddPRComment(): boolean {
|
||||||
|
return this.getPRCommentOption() !== JobSummaryOption.Never
|
||||||
|
}
|
||||||
|
|
||||||
shouldAddPRComment(hasFailure: boolean): boolean {
|
shouldAddPRComment(hasFailure: boolean): boolean {
|
||||||
return this.shouldAddJobSummary(this.getPRCommentOption(), hasFailure)
|
return this.shouldAddJobSummary(this.getPRCommentOption(), hasFailure)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as core from '@actions/core'
|
|||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
|
|
||||||
import {BuildResult} from './build-results'
|
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'
|
import {Deprecation, getDeprecations, getErrors} from './deprecation-collector'
|
||||||
|
|
||||||
export async function generateJobSummary(
|
export async function generateJobSummary(
|
||||||
@@ -33,6 +33,10 @@ export async function generateJobSummary(
|
|||||||
core.info('============================')
|
core.info('============================')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.canAddPRComment()) {
|
||||||
|
await minimizeObsoletePRComments()
|
||||||
|
}
|
||||||
|
|
||||||
if (config.shouldAddPRComment(hasFailure)) {
|
if (config.shouldAddPRComment(hasFailure)) {
|
||||||
await addPRComment(summaryTable)
|
await addPRComment(summaryTable)
|
||||||
}
|
}
|
||||||
@@ -48,7 +52,8 @@ async function addPRComment(jobSummary: string): Promise<void> {
|
|||||||
const pull_request_number = context.payload.pull_request.number
|
const pull_request_number = context.payload.pull_request.number
|
||||||
core.info(`Adding Job Summary as comment to PR #${pull_request_number}.`)
|
core.info(`Adding Job Summary as comment to PR #${pull_request_number}.`)
|
||||||
|
|
||||||
const prComment = `<h3>Job Summary for Gradle</h3>
|
const prComment = `${jobMarker(context)}
|
||||||
|
<h3>Job Summary for Gradle</h3>
|
||||||
<a href="${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}" target="_blank">
|
<a href="${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}" target="_blank">
|
||||||
<h5>${context.workflow} :: <em>${context.job}</em></h5>
|
<h5>${context.workflow} :: <em>${context.job}</em></h5>
|
||||||
</a>
|
</a>
|
||||||
@@ -57,6 +62,7 @@ ${jobSummary}`
|
|||||||
|
|
||||||
const github_token = getGithubToken()
|
const github_token = getGithubToken()
|
||||||
const octokit = github.getOctokit(github_token)
|
const octokit = github.getOctokit(github_token)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await octokit.rest.issues.createComment({
|
await octokit.rest.issues.createComment({
|
||||||
...context.repo,
|
...context.repo,
|
||||||
@@ -201,3 +207,77 @@ function truncateString(str: string, maxLength: number): string {
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function minimizeObsoletePRComments(): Promise<void> {
|
||||||
|
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: PullRequestComment[]
|
||||||
|
try {
|
||||||
|
const {repository} = await octokit.graphql<CommentsQueryResult>(query, {owner, repo, prNumber})
|
||||||
|
comments = repository.pullRequest?.comments?.nodes?.filter((c): c is PullRequestComment => 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jobMarker(context: typeof github.context): string {
|
||||||
|
const jobCorrelator = DependencyGraphConfig.constructJobCorrelator(context.workflow, context.job, getJobMatrix())
|
||||||
|
return `<!-- gradle-job-summary: ${jobCorrelator} -->`
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PullRequestComment {
|
||||||
|
id: string
|
||||||
|
body: string
|
||||||
|
isMinimized: boolean
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CommentsQueryResult {
|
||||||
|
repository: {
|
||||||
|
pullRequest?: {
|
||||||
|
comments?: {
|
||||||
|
nodes?: (PullRequestComment | null)[] | null
|
||||||
|
} | null
|
||||||
|
} | null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
import dedent from 'dedent'
|
import dedent from 'dedent'
|
||||||
import {describe, expect, it} from '@jest/globals'
|
import * as github from '@actions/github'
|
||||||
|
import {afterEach, describe, expect, it} from '@jest/globals'
|
||||||
|
|
||||||
import {BuildResult} from '../../src/build-results'
|
import {BuildResult} from '../../src/build-results'
|
||||||
import {renderSummaryTable} from '../../src/job-summary'
|
import {jobMarker, renderSummaryTable} from '../../src/job-summary'
|
||||||
|
|
||||||
|
const MATRIX_INPUT_ENV = 'INPUT_WORKFLOW-JOB-CONTEXT'
|
||||||
|
|
||||||
|
function fakeContext(workflow: string, job: string): typeof github.context {
|
||||||
|
return {workflow, job} as unknown as typeof github.context
|
||||||
|
}
|
||||||
|
|
||||||
const successfulHelpBuild: BuildResult = {
|
const successfulHelpBuild: BuildResult = {
|
||||||
rootProjectName: 'root',
|
rootProjectName: 'root',
|
||||||
@@ -177,3 +184,27 @@ describe('renderSummaryTable', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('jobMarker', () => {
|
||||||
|
const original = process.env[MATRIX_INPUT_ENV]
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (original === undefined) {
|
||||||
|
delete process.env[MATRIX_INPUT_ENV]
|
||||||
|
} else {
|
||||||
|
process.env[MATRIX_INPUT_ENV] = original
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('builds a hidden marker from the workflow and job', () => {
|
||||||
|
process.env[MATRIX_INPUT_ENV] = 'null'
|
||||||
|
const marker = jobMarker(fakeContext('CI', 'build'))
|
||||||
|
expect(marker).toBe('<!-- gradle-job-summary: ci-build -->')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('includes the job matrix in the marker', () => {
|
||||||
|
process.env[MATRIX_INPUT_ENV] = JSON.stringify({os: 'ubuntu', java: '17'})
|
||||||
|
const marker = jobMarker(fakeContext('CI', 'build'))
|
||||||
|
expect(marker).toBe('<!-- gradle-job-summary: ci-build-ubuntu-17 -->')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user