diff --git a/README.md b/README.md index 7b9a599..bc44a43 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,7 @@ can be replaced with: | `native-image-musl` | `'false'` | If set to `'true'`, sets up [musl] to build [static binaries][native-image-static] with GraalVM Native Image *(Linux only)*. [Example usage][native-image-musl-build] (be sure to replace `uses: ./` with `uses: graalvm/setup-graalvm@v1`). | | `native-image-job-reports` *) | `'false'` | If set to `'true'`, post a job summary containing a Native Image build report. | | `native-image-pr-reports` *) | `'false'` | If set to `'true'`, post a comment containing a Native Image build report on pull requests. Requires `write` permissions for the [`pull-requests` scope][gha-permissions]. | +| `native-image-pr-reports-update` *) | `'false'` | If set to `'true'`, updates old build report comment with new report on pull requests. Requires `native-image-pr-reports` to be `true` | | `components` | `''` | Comma-separated list of GraalVM components (e.g., `native-image` or `ruby,nodejs`) that will be installed by the [GraalVM Updater][gu]. | | `version` | `''` | `X.Y.Z` (e.g., `22.3.0`) for a specific [GraalVM release][releases] up to `22.3.2`
`mandrel-X.Y.Z.W` or `X.Y.Z.W-Final` (e.g., `mandrel-21.3.0.0-Final` or `21.3.0.0-Final`) for a specific [Mandrel release][mandrel-releases],
`mandrel-latest` or `latest` for the latest Mandrel stable release. | | `gds-token` | `''` | Download token for the GraalVM Download Service. If a non-empty token is provided, the action will set up GraalVM Enterprise Edition (see [GraalVM EE template](#template-for-graalvm-enterprise-edition)). | diff --git a/action.yml b/action.yml index dece5ee..ca39ddf 100644 --- a/action.yml +++ b/action.yml @@ -47,6 +47,10 @@ inputs: required: false description: 'Post a comment containing a Native Image build report on pull requests.' default: 'false' + native-image-pr-reports-update: + required: false + description: 'Instead of posting another comment, update an existing PR comment with the latest Native Image build report.' + default: 'false' version: required: false description: 'GraalVM version (release, latest, dev).' diff --git a/src/features/reports.ts b/src/features/reports.ts index c2d1365..5ae2599 100644 --- a/src/features/reports.ts +++ b/src/features/reports.ts @@ -5,7 +5,13 @@ import * as github from '@actions/github' import * as semver from 'semver' import {join} from 'path' import {tmpdir} from 'os' -import {createPRComment, isPREvent, toSemVer} from '../utils' +import { + createPRComment, + findExistingPRCommentId, + isPREvent, + toSemVer, + updatePRComment +} from '../utils' const BUILD_OUTPUT_JSON_PATH = join(tmpdir(), 'native-image-build-output.json') const BYTES_TO_KiB = 1024 @@ -15,12 +21,14 @@ const DOCS_BASE = 'https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md' const INPUT_NI_JOB_REPORTS = 'native-image-job-reports' const INPUT_NI_PR_REPORTS = 'native-image-pr-reports' +const INPUT_NI_PR_REPORTS_UPDATE = 'native-image-pr-reports-update' const NATIVE_IMAGE_CONFIG_FILE = join( tmpdir(), 'native-image-options.properties' ) const NATIVE_IMAGE_OPTIONS_ENV = 'NATIVE_IMAGE_OPTIONS' const NATIVE_IMAGE_CONFIG_FILE_ENV = 'NATIVE_IMAGE_CONFIG_FILE' +const PR_COMMENT_TITLE = '## GraalVM Native Image Build Report' interface AnalysisResult { total: number @@ -134,7 +142,18 @@ export async function generateReports(): Promise { core.summary.write() } if (arePRReportsEnabled()) { - createPRComment(report) + if (arePRReportsUpdateEnabled()) { + const commentId = await findExistingPRCommentId(PR_COMMENT_TITLE) + if (commentId) { + return updatePRComment(report, commentId) + } + } + return createPRComment(report) + } else if (arePRReportsUpdateEnabled()) { + core.error('Build report summary:') + throw new Error( + `'native-image-pr-reports-update' option requires 'native-image-pr-reports' to be set 'true'` + ) } } } @@ -147,6 +166,10 @@ function arePRReportsEnabled(): boolean { return isPREvent() && core.getInput(INPUT_NI_PR_REPORTS) === 'true' } +function arePRReportsUpdateEnabled(): boolean { + return isPREvent() && core.getInput(INPUT_NI_PR_REPORTS_UPDATE) === 'true' +} + function setNativeImageOption( javaVersionOrDev: string, optionValue: string @@ -260,7 +283,7 @@ function createReport(data: BuildOutput): string { )} of total time)` } - return `## GraalVM Native Image Build Report + return `${PR_COMMENT_TITLE} \`${info.name}\` generated${totalTime} as part of the '${ context.job diff --git a/src/utils.ts b/src/utils.ts index 1bd5335..655d1d9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -184,6 +184,52 @@ function getGitHubToken(): string { return core.getInput(c.INPUT_GITHUB_TOKEN) } +export async function findExistingPRCommentId( + bodyStartsWith: string +): Promise { + if (!isPREvent()) { + throw new Error('Not a PR event.') + } + + const context = github.context + const octokit = github.getOctokit(getGitHubToken()) + try { + const comments = await octokit.paginate(octokit.rest.issues.listComments, { + ...context.repo, + issue_number: context.payload.pull_request?.number as number + }) + const matchingComment = comments.reverse().find(comment => { + return comment.body && comment.body.startsWith(bodyStartsWith) + }) + return matchingComment ? matchingComment.id : undefined + } catch (err) { + core.error( + `Failed to list pull request comments. Please make sure this job has 'write' permissions for the 'pull-requests' scope (see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions)? Internal error: ${err}` + ) + } +} + +export async function updatePRComment( + content: string, + commentId: number +): Promise { + if (!isPREvent()) { + throw new Error('Not a PR event.') + } + + try { + await github.getOctokit(getGitHubToken()).rest.issues.updateComment({ + ...github.context.repo, + comment_id: commentId, + body: content + }) + } catch (err) { + core.error( + `Failed to update pull request comment. Please make sure this job has 'write' permissions for the 'pull-requests' scope (see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions)? Internal error: ${err}` + ) + } +} + export async function createPRComment(content: string): Promise { if (!isPREvent()) { throw new Error('Not a PR event.')