diff --git a/README.md b/README.md index 1156e1d2..58eb75fd 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,13 @@ The option to disable push to a branch. When set to false, the `branch` option i The option how to render changed file in comment. This action will change PR and workflow summary report format. Available options are `raw` and `summarized`. `raw` will render report comment with expanded results. `summarized` will render report comment using `
` tag to summarize by changed files. +#### `outdated-comment-action` (Optional) + +- Type: String +- Default: `"none"` + +The option to handle outdated comments in the PR. Available options are `none` and `minimize`. `none` do nothing. `minimize` will minimize outdated action comments. + ## Limitation - If the `artifact` is deleted, the report will also be deleted, see [`Artifact and log retention policy`](https://docs.github.com/ja/actions/learn-github-actions/usage-limits-billing-and-administration#artifact-and-log-retention-policy) for the retention period of the `artifact`. diff --git a/dist/action.yml b/dist/action.yml index 0dbb60cf..8a8dd362 100644 --- a/dist/action.yml +++ b/dist/action.yml @@ -44,6 +44,9 @@ inputs: comment-report-format: description: "The option how to render changed file in comment. `raw` by default." required: false + outdated-comment-action: + description: "The option to handle outdated comments. `none` by default." + required: false runs: using: "node20" main: "lib/index.js" diff --git a/src/client.ts b/src/client.ts index 4c67e738..432f127b 100644 --- a/src/client.ts +++ b/src/client.ts @@ -44,6 +44,45 @@ export const createClient = (repository: Repository, octokit: Octokit) => { { numOfAttempts: 5 }, ); }, + listComments: async (issueNumber: number): Promise<{ node_id: string; body?: string | undefined }[]> => { + return backOff( + () => + octokit.paginate( + octokit.rest.issues.listComments, + { + ...repository, + issue_number: issueNumber, + per_page: 100, + }, + r => r.data, + ), + { numOfAttempts: 5 }, + ); + }, + minimizeOutdatedComment: async (nodeId: string) => { + const _ = await backOff( + () => + octokit.graphql( + ` +mutation($input: MinimizeCommentInput!) { + minimizeComment(input: $input) { + minimizedComment { + isMinimized + } + } +} +`, + { + input: { + subjectId: nodeId, + classifier: 'OUTDATED', + }, + }, + ), + { numOfAttempts: 5 }, + ); + return; + }, postComment: async (issueNumber: number, comment: string) => { const _ = await backOff( () => octokit.rest.issues.createComment({ ...repository, issue_number: issueNumber, body: comment }), diff --git a/src/comment.ts b/src/comment.ts index 8b61c264..78244630 100644 --- a/src/comment.ts +++ b/src/comment.ts @@ -283,3 +283,9 @@ ${report} return body; }; + +export const isRegActionComment = ({ artifactName, body }: { artifactName: string; body: string }): boolean => { + return ( + body.includes(`## ArtifactName: \`${artifactName}\``) || body.includes(`## ArtifactName: [\`${artifactName}\`]`) + ); +}; diff --git a/src/config.ts b/src/config.ts index 4d58a243..1106c4e7 100644 --- a/src/config.ts +++ b/src/config.ts @@ -17,6 +17,7 @@ export interface Config { customReportPage: string | null; reportFilePath: string | null; commentReportFormat: 'raw' | 'summarized'; + outdatedCommentAction: 'none' | 'minimize'; } const validateGitHubToken = (githubToken: string | undefined) => { @@ -101,6 +102,12 @@ function validateCommentReportFormat(format: string): asserts format is 'raw' | } } +function validateOutdatedCommentAction(action: string): asserts action is 'none' | 'minimize' { + if (action !== 'none' && action !== 'minimize') { + throw new Error(`'outdated-comment-action' input must be 'none' or 'minimized' but got '${action}'`); + } +} + export const getConfig = (): Config => { const githubToken = core.getInput('github-token'); const imageDirectoryPath = core.getInput('image-directory-path'); @@ -121,6 +128,8 @@ export const getConfig = (): Config => { validateReportFilePath(reportFilePath); const commentReportFormat = core.getInput('comment-report-format') || 'raw'; validateCommentReportFormat(commentReportFormat); + const outdatedCommentAction = core.getInput('outdated-comment-action') || 'none'; + validateOutdatedCommentAction(outdatedCommentAction); return { githubToken, @@ -136,5 +145,6 @@ export const getConfig = (): Config => { customReportPage, reportFilePath, commentReportFormat, + outdatedCommentAction, }; }; diff --git a/src/service.ts b/src/service.ts index 3263952e..023186d8 100644 --- a/src/service.ts +++ b/src/service.ts @@ -10,7 +10,7 @@ import { Config } from './config'; import { Event } from './event'; import { findRunAndArtifact, RunClient } from './run'; import { compare, CompareOutput } from './compare'; -import { createCommentWithTarget, createCommentWithoutTarget } from './comment'; +import { createCommentWithTarget, createCommentWithoutTarget, isRegActionComment } from './comment'; import * as constants from './constants'; import { workspace } from './path'; import { pushImages } from './push'; @@ -115,6 +115,17 @@ const init = async (config: Config) => { type CommentClient = { postComment: (issueNumber: number, comment: string) => Promise; + listComments: (issueNumber: number) => Promise<{ node_id: string; body?: string | undefined }[]>; + minimizeOutdatedComment: (nodeId: string) => Promise; +}; + +const minimizePreviousComments = async (client: CommentClient, issueNumber: number, artifactName: string) => { + const comments = await client.listComments(issueNumber); + for (const comment of comments) { + if (comment.body && isRegActionComment({ artifactName, body: comment.body })) { + await client.minimizeOutdatedComment(comment.node_id); + } + } }; type SummaryClient = { @@ -172,6 +183,9 @@ export const run = async ({ artifactName: config.artifactName, customReportPage: config.customReportPage, }); + if (config.outdatedCommentAction === 'minimize') { + await minimizePreviousComments(client, event.number, config.artifactName); + } await client.postComment(event.number, comment); } return; @@ -218,6 +232,9 @@ export const run = async ({ }); try { + if (config.outdatedCommentAction === 'minimize') { + await minimizePreviousComments(client, event.number, config.artifactName); + } await client.postComment(event.number, comment); log.info('post summary comment');