Merge pull request #1021 from gradle/dd/pr-comment

Automatically add Job Summary as PR comment
This commit is contained in:
Daz DeBoer 2024-01-02 01:51:30 +01:00 committed by GitHub
commit 4faac45dc8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 544 additions and 296 deletions

View file

@ -31,6 +31,8 @@ jobs:
dependency-graph: dependency-graph:
uses: ./.github/workflows/integ-test-dependency-graph.yml uses: ./.github/workflows/integ-test-dependency-graph.yml
permissions:
contents: write
with: with:
cache-key-prefix: ${{github.run_number}}- cache-key-prefix: ${{github.run_number}}-

View file

@ -53,6 +53,8 @@ jobs:
dependency-graph: dependency-graph:
needs: build-distribution needs: build-distribution
uses: ./.github/workflows/integ-test-dependency-graph.yml uses: ./.github/workflows/integ-test-dependency-graph.yml
permissions:
contents: write
with: with:
runner-os: '["ubuntu-latest"]' runner-os: '["ubuntu-latest"]'
download-dist: true download-dist: true

View file

@ -8,7 +8,7 @@ env:
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
jobs: jobs:
run-gradle-builds: many-gradle-builds:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout sources - name: Checkout sources
@ -42,6 +42,27 @@ jobs:
continue-on-error: true continue-on-error: true
run: ./gradlew not-a-real-task run: ./gradlew not-a-real-task
successful-builds-with-no-summary:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4
- name: Build distribution
shell: bash
run: |
npm install
npm run build
- name: Setup Gradle
uses: ./
with:
add-job-summary: on-failure
- name: Build kotlin-dsl project
working-directory: .github/workflow-samples/kotlin-dsl
run: ./gradlew assemble
- name: Build kotlin-dsl project without Build Scan®
working-directory: .github/workflow-samples/kotlin-dsl
run: ./gradlew assemble check --no-scan
pre-existing-gradle-home: pre-existing-gradle-home:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View file

@ -2,26 +2,50 @@ name: Demo adding Build Scan® comment to PR
on: on:
pull_request: pull_request:
types: [assigned, review_requested] types: [assigned, review_requested]
permissions:
pull-requests: write
jobs: jobs:
gradle: successful-build-with-always-comment:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout project sources - name: Checkout project sources
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Gradle - name: Setup Gradle
uses: ./ uses: ./
with:
add-job-summary-as-pr-comment: always
- name: Run build with Gradle wrapper - name: Run build with Gradle wrapper
id: gradle id: gradle
working-directory: .github/workflow-samples/kotlin-dsl working-directory: .github/workflow-samples/kotlin-dsl
run: ./gradlew build --scan run: ./gradlew build --scan
- name: "Add Build Scan URL as PR comment"
uses: actions/github-script@v7 successful-build-with-comment-on-failure:
runs-on: ubuntu-latest
steps:
- name: Checkout project sources
uses: actions/checkout@v4
- name: Setup Gradle
uses: ./
with: with:
github-token: ${{secrets.GITHUB_TOKEN}} add-job-summary-as-pr-comment: on-failure
script: | - name: Run build with Gradle wrapper
github.rest.issues.createComment({ id: gradle
issue_number: context.issue.number, working-directory: .github/workflow-samples/kotlin-dsl
owner: context.repo.owner, run: ./gradlew build --scan
repo: context.repo.repo,
body: 'PR ready for review: ${{ steps.gradle.outputs.build-scan-url }}' failing-build-with-comment-on-failure:
}) runs-on: ubuntu-latest
steps:
- name: Checkout project sources
uses: actions/checkout@v4
- name: Setup Gradle
uses: ./
with:
add-job-summary-as-pr-comment: on-failure
- name: Run build with Gradle wrapper
id: gradle
working-directory: .github/workflow-samples/kotlin-dsl
run: ./gradlew no-a-real-task --scan
continue-on-error: true

View file

@ -12,6 +12,9 @@ on:
type: boolean type: boolean
default: false default: false
permissions:
contents: write
env: env:
DOWNLOAD_DIST: ${{ inputs.download-dist }} DOWNLOAD_DIST: ${{ inputs.download-dist }}
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: dependency-graph-${{ inputs.cache-key-prefix }} GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: dependency-graph-${{ inputs.cache-key-prefix }}

View file

@ -368,62 +368,57 @@ gradle-home-cache-cleanup: true
## Build reporting ## Build reporting
The `gradle-build-action` collects information about any Gradle executions that occur in a workflow, and reports these via The `gradle-build-action` collects information about any Gradle executions that occur in a workflow, including the root project,
a Job Summary, visible in the GitHub Actions UI. For each Gradle execution, details about the invocation are listed, together with requested tasks, build outcome and any Build Scan link generated. Details of cache entries read and written are also collected.
a link to any Build Scan® published. These details are compiled into a Job Summary, which is visible in the GitHub Actions UI.
Generation of a Job Summary is enabled by default. If this is not desired, it can be disable as follows: Generation of a Job Summary is enabled by default for all Jobs using the `gradle-build-action`. This feature can be configured
so that a Job Summary is never generated, or so that a Job Summary is only generated on build failure:
```yaml ```yaml
generate-job-summary: false add-job-summary: 'on-failure' # Valid values are 'always' (default), 'never', and 'on-failure'
``` ```
Note that the action collects information about Gradle invocations via an [Initialization Script](https://docs.gradle.org/current/userguide/init_scripts.html#sec:using_an_init_script) ### Adding Job Summary as a Pull Request comment
located at `USER_HOME/.gradle/init.d/build-result-capture.init.gradle`.
If you are using init scripts for the [Gradle Enterprise Gradle Plugin](https://plugins.gradle.org/plugin/com.gradle.enterprise) like
[`scans-init.gradle` or `gradle-enterprise-init.gradle`](https://docs.gradle.com/enterprise/gradle-plugin/#scans_gradle_com),
you'll need to ensure these files are applied prior to `build-result-capture.init.gradle`.
Since Gradle applies init scripts in alphabetical order, one way to ensure this is via file naming.
### Build Scan® link as Step output It is sometimes more convenient to view the results of a GitHub Actions Job directly from the Pull Request that triggered
the Job. For this purpose you can configure the action so that Job Summary data is added as a Pull Request comment.
As well as reporting the [Build Scan](https://gradle.com/build-scans/) link in the Job Summary,
the `gradle-build-action` action makes this link available as a Step output named `build-scan-url`.
You can then use that link in subsequent actions of your workflow. For example:
```yaml ```yaml
# .github/workflows/gradle-build-pr.yml name: CI
name: Run Gradle on PRs on:
on: pull_request pull_request:
permissions:
pull-requests: write
jobs: jobs:
gradle: run-gradle-build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout project sources - name: Checkout project sources
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Gradle - name: Setup Gradle
uses: gradle/gradle-build-action@v2 uses: gradle/gradle-build-action@v3
- name: Run build with Gradle wrapper
id: gradle
run: ./gradlew build --scan
- name: "Add Build Scan URL as PR comment"
uses: actions/github-script@v5
if: github.event_name == 'pull_request' && failure()
with: with:
github-token: ${{secrets.GITHUB_TOKEN}} add-job-summary-as-pr-comment: on-failure # Valid values are 'never' (default), 'always', and 'on-failure'
script: | - run: ./gradlew build --scan
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '❌ ${{ github.workflow }} failed: ${{ steps.gradle.outputs.build-scan-url }}'
})
``` ```
### Saving build outputs Note that in order to add a Pull Request comment, the workflow must be configured with the `pull-requests: write` permission.
By default, a GitHub Actions workflow using `gradle-build-action` will record the log output and any Build Scan links for your build,
but any output files generated by the build will not be saved. ### Build Scan® link as Step output
As well as reporting all [Build Scan](https://gradle.com/build-scans/) links in the Job Summary,
the `gradle-build-action` action makes this link available an an output of any Step that executes Gradle.
The output name is `build-scan-url`. You can then use the build scan link in subsequent actions of your workflow.
### Saving arbitrary build outputs
By default, a GitHub Actions workflow using `gradle-build-action` will record the log output and any Build Scan
links for your build, but any output files generated by the build will not be saved.
To save selected files from your build execution, you can use the core [Upload-Artifact](https://github.com/actions/upload-artifact) action. To save selected files from your build execution, you can use the core [Upload-Artifact](https://github.com/actions/upload-artifact) action.
For example: For example:
@ -447,6 +442,14 @@ jobs:
path: build/reports/ path: build/reports/
``` ```
### Use of custom init-scripts in Gradle User Home
Note that the action collects information about Gradle invocations via an [Initialization Script](https://docs.gradle.org/current/userguide/init_scripts.html#sec:using_an_init_script)
located at `USER_HOME/.gradle/init.d/gradle-build-action.build-result-capture.init.gradle`.
If you are adding any custom init scripts to the `USER_HOME/.gradle/init.d` directory, it may be necessary to ensure these files are applied prior to `gradle-build-action.build-result-capture.init.gradle`.
Since Gradle applies init scripts in alphabetical order, one way to ensure this is via file naming.
## Support for GitHub Enterprise Server (GHES) ## Support for GitHub Enterprise Server (GHES)
You can use the `gradle-build-action` on GitHub Enterprise Server, and benefit from the improved integration with Gradle. Depending on the version of GHES you are running, certain features may be limited: You can use the `gradle-build-action` on GitHub Enterprise Server, and benefit from the improved integration with Gradle. Depending on the version of GHES you are running, certain features may be limited:

View file

@ -58,10 +58,15 @@ inputs:
required: false required: false
default: false default: false
generate-job-summary: add-job-summary:
description: When 'false', no Job Summary will be generated for the Job. description: Specifies when a Job Summary should be inluded in the action results. Valid values are 'never', 'always' (default), and 'on-failure'.
required: false required: false
default: true default: 'always'
add-job-summary-as-pr-comment:
description: Specifies when each Job Summary should be added as a PR comment. Valid values are 'never' (default), 'always', and 'on-failure'. No action will be taken if the workflow was not triggered from a pull request.
required: false
default: 'never'
dependency-graph: dependency-graph:
description: Specifies if a GitHub dependency snapshot should be generated for each Gradle build, and if so, how. Valid values are 'disabled' (default), 'generate', 'generate-and-submit', 'generate-and-upload' and 'download-and-submit'. description: Specifies if a GitHub dependency snapshot should be generated for each Gradle build, and if so, how. Valid values are 'disabled' (default), 'generate', 'generate-and-submit', 'generate-and-upload' and 'download-and-submit'.
@ -88,6 +93,12 @@ inputs:
required: false required: false
deprecation-message: Using the action to execute Gradle directly is deprecated in favor of using the action to setup Gradle, and executing Gradle in a subsequent Step. See https://github.com/gradle/gradle-build-action?tab=readme-ov-file#use-the-action-to-setup-gradle. deprecation-message: Using the action to execute Gradle directly is deprecated in favor of using the action to setup Gradle, and executing Gradle in a subsequent Step. See https://github.com/gradle/gradle-build-action?tab=readme-ov-file#use-the-action-to-setup-gradle.
generate-job-summary:
description: When 'false', no Job Summary will be generated for the Job.
required: false
default: true
deprecation-message: Superceded by the new 'add-job-summary' and 'add-job-summary-as-pr-comment' parameters.
# EXPERIMENTAL & INTERNAL ACTION INPUTS # EXPERIMENTAL & INTERNAL ACTION INPUTS
# The following action properties allow fine-grained tweaking of the action caching behaviour. # The following action properties allow fine-grained tweaking of the action caching behaviour.
# These properties are experimental and not (yet) designed for production use, and may change without notice in a subsequent release of `gradle-build-action`. # These properties are experimental and not (yet) designed for production use, and may change without notice in a subsequent release of `gradle-build-action`.

223
dist/main/index.js vendored
View file

@ -139284,8 +139284,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.logCachingReport = exports.writeCachingReport = exports.CacheEntryListener = exports.CacheListener = void 0; exports.generateCachingReport = exports.CacheEntryListener = exports.CacheListener = void 0;
const core = __importStar(__nccwpck_require__(42186));
const cache = __importStar(__nccwpck_require__(27799)); const cache = __importStar(__nccwpck_require__(27799));
class CacheListener { class CacheListener {
constructor() { constructor() {
@ -139373,37 +139372,36 @@ class CacheEntryListener {
} }
} }
exports.CacheEntryListener = CacheEntryListener; exports.CacheEntryListener = CacheEntryListener;
function writeCachingReport(listener) { function generateCachingReport(listener) {
const entries = listener.cacheEntries; const entries = listener.cacheEntries;
core.summary.addRaw(`\n<details><summary><h4>Caching for gradle-build-action was ${listener.cacheStatus} - expand for details</h4></summary>\n`); return `
core.summary.addTable([ <details>
[ <summary><h4>Caching for gradle-build-action was ${listener.cacheStatus} - expand for details</h4></summary>
{ data: '', header: true }, ${renderEntryTable(entries)}
{ data: 'Count', header: true },
{ data: 'Total Size (Mb)', header: true } <h5>Cache Entry Details</h5>
], <pre>
['Entries Restored', `${getCount(entries, e => e.restoredSize)}`, `${getSize(entries, e => e.restoredSize)}`], ${renderEntryDetails(listener)}
['Entries Saved', `${getCount(entries, e => e.savedSize)}`, `${getSize(entries, e => e.savedSize)}`]
]);
core.summary.addHeading('Cache Entry Details', 5);
const entryDetails = renderEntryDetails(listener);
core.summary.addRaw(`<pre>
${entryDetails}
</pre> </pre>
</details> </details>
`); `;
} }
exports.writeCachingReport = writeCachingReport; exports.generateCachingReport = generateCachingReport;
function logCachingReport(listener) { function renderEntryTable(entries) {
const entries = listener.cacheEntries; return `
core.startGroup(`Caching for gradle-build-action was ${listener.cacheStatus} - expand for details`); <table>
core.info(`Entries Restored: ${getCount(entries, e => e.restoredSize)} (${getSize(entries, e => e.restoredSize)} Mb)`); <tr><td></td><th>Count</th><th>Total Size (Mb)</th></tr>
core.info(`Entries Saved : ${getCount(entries, e => e.savedSize)} (${getSize(entries, e => e.savedSize)} Mb)`); <tr><td>Entries Restored</td>
core.info(`Cache Entry Details`); <td>${getCount(entries, e => e.restoredSize)}</td>
core.info(renderEntryDetails(listener)); <td>${getSize(entries, e => e.restoredSize)}</td>
core.endGroup(); </tr>
<tr><td>Entries Saved</td>
<td>${getCount(entries, e => e.savedSize)}</td>
<td>${getSize(entries, e => e.savedSize)}</td>
</tr>
</table>
`;
} }
exports.logCachingReport = logCachingReport;
function renderEntryDetails(listener) { function renderEntryDetails(listener) {
return listener.cacheEntries return listener.cacheEntries
.map(entry => { .map(entry => {
@ -140344,7 +140342,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.DependencyGraphOption = exports.parseNumericInput = exports.getArtifactRetentionDays = exports.getDependencyGraphOption = exports.isDependencyGraphEnabled = exports.isJobSummaryEnabled = exports.getGithubToken = exports.getJobMatrix = exports.getArguments = exports.getGradleExecutable = exports.getGradleVersion = exports.getBuildRootDirectory = exports.getCacheExcludes = exports.getCacheIncludes = exports.getCacheEncryptionKey = exports.isCacheCleanupEnabled = exports.isCacheDebuggingEnabled = exports.isCacheStrictMatch = exports.isCacheOverwriteExisting = exports.isCacheWriteOnly = exports.isCacheReadOnly = exports.isCacheDisabled = void 0; exports.JobSummaryOption = exports.DependencyGraphOption = exports.parseNumericInput = exports.getArtifactRetentionDays = exports.getDependencyGraphOption = exports.getPRCommentOption = exports.getJobSummaryOption = exports.isJobSummaryEnabled = exports.getGithubToken = exports.getJobMatrix = exports.getArguments = exports.getGradleExecutable = exports.getGradleVersion = exports.getBuildRootDirectory = exports.getCacheExcludes = exports.getCacheIncludes = exports.getCacheEncryptionKey = exports.isCacheCleanupEnabled = exports.isCacheDebuggingEnabled = exports.isCacheStrictMatch = exports.isCacheOverwriteExisting = exports.isCacheWriteOnly = exports.isCacheReadOnly = exports.isCacheDisabled = void 0;
const core = __importStar(__nccwpck_require__(42186)); const core = __importStar(__nccwpck_require__(42186));
const string_argv_1 = __nccwpck_require__(19663); const string_argv_1 = __nccwpck_require__(19663);
function isCacheDisabled() { function isCacheDisabled() {
@ -140416,10 +140414,26 @@ function isJobSummaryEnabled() {
return getBooleanInput('generate-job-summary', true); return getBooleanInput('generate-job-summary', true);
} }
exports.isJobSummaryEnabled = isJobSummaryEnabled; exports.isJobSummaryEnabled = isJobSummaryEnabled;
function isDependencyGraphEnabled() { function getJobSummaryOption() {
return getBooleanInput('generate-dependency-graph', true); return parseJobSummaryOption('add-job-summary');
}
exports.getJobSummaryOption = getJobSummaryOption;
function getPRCommentOption() {
return parseJobSummaryOption('add-job-summary-as-pr-comment');
}
exports.getPRCommentOption = getPRCommentOption;
function parseJobSummaryOption(paramName) {
const val = core.getInput(paramName);
switch (val.toLowerCase().trim()) {
case 'never':
return JobSummaryOption.Never;
case 'always':
return JobSummaryOption.Always;
case 'on-failure':
return JobSummaryOption.OnFailure;
}
throw TypeError(`The value '${val}' is not valid for ${paramName}. Valid values are: [never, always, on-failure].`);
} }
exports.isDependencyGraphEnabled = isDependencyGraphEnabled;
function getDependencyGraphOption() { function getDependencyGraphOption() {
const val = core.getInput('dependency-graph'); const val = core.getInput('dependency-graph');
switch (val.toLowerCase().trim()) { switch (val.toLowerCase().trim()) {
@ -140434,7 +140448,7 @@ function getDependencyGraphOption() {
case 'download-and-submit': case 'download-and-submit':
return DependencyGraphOption.DownloadAndSubmit; return DependencyGraphOption.DownloadAndSubmit;
} }
throw TypeError(`The value '${val} is not valid for 'dependency-graph. Valid values are: [disabled, generate, generate-and-submit, generate-and-upload, download-and-submit]. The default value is 'disabled'.`); throw TypeError(`The value '${val}' is not valid for 'dependency-graph'. Valid values are: [disabled, generate, generate-and-submit, generate-and-upload, download-and-submit]. The default value is 'disabled'.`);
} }
exports.getDependencyGraphOption = getDependencyGraphOption; exports.getDependencyGraphOption = getDependencyGraphOption;
function getArtifactRetentionDays() { function getArtifactRetentionDays() {
@ -140473,6 +140487,12 @@ var DependencyGraphOption;
DependencyGraphOption["GenerateAndUpload"] = "generate-and-upload"; DependencyGraphOption["GenerateAndUpload"] = "generate-and-upload";
DependencyGraphOption["DownloadAndSubmit"] = "download-and-submit"; DependencyGraphOption["DownloadAndSubmit"] = "download-and-submit";
})(DependencyGraphOption || (exports.DependencyGraphOption = DependencyGraphOption = {})); })(DependencyGraphOption || (exports.DependencyGraphOption = DependencyGraphOption = {}));
var JobSummaryOption;
(function (JobSummaryOption) {
JobSummaryOption["Never"] = "never";
JobSummaryOption["Always"] = "always";
JobSummaryOption["OnFailure"] = "on-failure";
})(JobSummaryOption || (exports.JobSummaryOption = JobSummaryOption = {}));
/***/ }), /***/ }),
@ -140515,48 +140535,89 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.logJobSummary = exports.writeJobSummary = void 0; exports.generateJobSummary = void 0;
const core = __importStar(__nccwpck_require__(42186)); const core = __importStar(__nccwpck_require__(42186));
const github = __importStar(__nccwpck_require__(95438));
const summary_1 = __nccwpck_require__(81327);
const request_error_1 = __nccwpck_require__(10537);
const params = __importStar(__nccwpck_require__(23885));
const cache_reporting_1 = __nccwpck_require__(66674); const cache_reporting_1 = __nccwpck_require__(66674);
function writeJobSummary(buildResults, cacheListener) { function generateJobSummary(buildResults, cacheListener) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
core.info('Writing job summary'); const summaryTable = renderSummaryTable(buildResults);
if (buildResults.length === 0) { const cachingReport = (0, cache_reporting_1.generateCachingReport)(cacheListener);
core.debug('No Gradle build results found. Summary table will not be generated.'); if (shouldGenerateJobSummary(buildResults)) {
} core.info('Generating Job Summary');
else { core.summary.addRaw(summaryTable);
writeSummaryTable(buildResults); core.summary.addRaw(cachingReport);
}
(0, cache_reporting_1.writeCachingReport)(cacheListener);
yield core.summary.write(); yield core.summary.write();
});
}
exports.writeJobSummary = writeJobSummary;
function logJobSummary(buildResults, cacheListener) {
return __awaiter(this, void 0, void 0, function* () {
if (buildResults.length === 0) {
core.debug('No Gradle build results found. Summary table will not be logged.');
} }
else { else {
logSummaryTable(buildResults); core.info('============================');
core.info(summaryTable);
core.info('============================');
core.info(cachingReport);
core.info('============================');
}
if (shouldAddPRComment(buildResults)) {
yield addPRComment(summaryTable);
} }
(0, cache_reporting_1.logCachingReport)(cacheListener);
}); });
} }
exports.logJobSummary = logJobSummary; exports.generateJobSummary = generateJobSummary;
function writeSummaryTable(results) { function addPRComment(jobSummary) {
core.summary.addHeading('Gradle Builds', 3); return __awaiter(this, void 0, void 0, function* () {
core.summary.addRaw(` const context = github.context;
if (context.payload.pull_request == null) {
core.info('No pull_request trigger: not adding PR comment');
return;
}
const pull_request_number = context.payload.pull_request.number;
core.info(`Adding Job Summary as comment to PR #${pull_request_number}.`);
const prComment = `<h3>Job Summary for gradle-build-action</h3>
<h5>${github.context.workflow} :: <em>${github.context.job}</em></h5>
${jobSummary}`;
const github_token = params.getGithubToken();
const octokit = github.getOctokit(github_token);
try {
yield octokit.rest.issues.createComment(Object.assign(Object.assign({}, context.repo), { issue_number: pull_request_number, body: prComment }));
}
catch (error) {
if (error instanceof request_error_1.RequestError) {
core.warning(buildWarningMessage(error));
}
else {
throw error;
}
}
});
}
function buildWarningMessage(error) {
const mainWarning = `Failed to generate PR comment.\n${String(error)}`;
if (error.message === 'Resource not accessible by integration') {
return `${mainWarning}
Please ensure that the 'pull-requests: write' permission is available for the workflow job.
Note that this permission is never available for a workflow triggered from a repository fork.
`;
}
return mainWarning;
}
function renderSummaryTable(results) {
if (results.length === 0) {
return 'No Gradle build results detected.';
}
return `
<table> <table>
<tr> <tr>
<th>Root Project</th> <th>Gradle Root Project</th>
<th>Requested Tasks</th> <th>Requested Tasks</th>
<th>Gradle Version</th> <th>Gradle Version</th>
<th>Build Outcome</th> <th>Build Outcome</th>
<th>Build Scan®</th> <th>Build Scan®</th>
</tr>${results.map(result => renderBuildResultRow(result)).join('')} </tr>${results.map(result => renderBuildResultRow(result)).join('')}
</table> </table>
`); `;
} }
function renderBuildResultRow(result) { function renderBuildResultRow(result) {
return ` return `
@ -140585,16 +140646,28 @@ function renderBuildScanBadge(outcomeText, outcomeColor, targetUrl) {
const badgeHtml = `<img src="${badgeUrl}" alt="Build Scan ${outcomeText}" />`; const badgeHtml = `<img src="${badgeUrl}" alt="Build Scan ${outcomeText}" />`;
return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`; return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`;
} }
function logSummaryTable(results) { function shouldGenerateJobSummary(buildResults) {
core.info('============================'); if (!process.env[summary_1.SUMMARY_ENV_VAR]) {
core.info('Gradle Builds'); return false;
core.info('----------------------------'); }
core.info('Root Project | Requested Tasks | Gradle Version | Build Outcome | Build Scan®'); if (!params.isJobSummaryEnabled()) {
core.info('----------------------------'); return false;
for (const result of results) { }
core.info(`${result.rootProjectName} | ${result.requestedTasks} | ${result.gradleVersion} | ${result.buildFailed ? 'FAILED' : 'SUCCESS'} | ${result.buildScanFailed ? 'Publish failed' : result.buildScanUri}`); return shouldAddJobSummary(params.getJobSummaryOption(), buildResults);
}
function shouldAddPRComment(buildResults) {
return shouldAddJobSummary(params.getPRCommentOption(), buildResults);
}
function shouldAddJobSummary(option, buildResults) {
switch (option) {
case params.JobSummaryOption.Always:
return true;
case params.JobSummaryOption.Never:
return false;
case params.JobSummaryOption.OnFailure:
core.info(`Got these build results: ${JSON.stringify(buildResults)}`);
return buildResults.some(result => result.buildFailed);
} }
core.info('============================');
} }
@ -140990,14 +141063,13 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.complete = exports.setup = void 0; exports.complete = exports.setup = void 0;
const core = __importStar(__nccwpck_require__(42186)); const core = __importStar(__nccwpck_require__(42186));
const exec = __importStar(__nccwpck_require__(71514)); const exec = __importStar(__nccwpck_require__(71514));
const summary_1 = __nccwpck_require__(81327);
const path = __importStar(__nccwpck_require__(71017)); const path = __importStar(__nccwpck_require__(71017));
const os = __importStar(__nccwpck_require__(22037)); const os = __importStar(__nccwpck_require__(22037));
const caches = __importStar(__nccwpck_require__(23800)); const caches = __importStar(__nccwpck_require__(23800));
const layout = __importStar(__nccwpck_require__(28182)); const layout = __importStar(__nccwpck_require__(28182));
const params = __importStar(__nccwpck_require__(23885)); const params = __importStar(__nccwpck_require__(23885));
const dependencyGraph = __importStar(__nccwpck_require__(80)); const dependencyGraph = __importStar(__nccwpck_require__(80));
const job_summary_1 = __nccwpck_require__(87345); const jobSummary = __importStar(__nccwpck_require__(87345));
const build_results_1 = __nccwpck_require__(82107); const build_results_1 = __nccwpck_require__(82107);
const cache_reporting_1 = __nccwpck_require__(66674); const cache_reporting_1 = __nccwpck_require__(66674);
const daemon_controller_1 = __nccwpck_require__(85146); const daemon_controller_1 = __nccwpck_require__(85146);
@ -141033,12 +141105,7 @@ function complete() {
const cacheListener = cache_reporting_1.CacheListener.rehydrate(core.getState(CACHE_LISTENER)); const cacheListener = cache_reporting_1.CacheListener.rehydrate(core.getState(CACHE_LISTENER));
const daemonController = new daemon_controller_1.DaemonController(buildResults); const daemonController = new daemon_controller_1.DaemonController(buildResults);
yield caches.save(gradleUserHome, cacheListener, daemonController); yield caches.save(gradleUserHome, cacheListener, daemonController);
if (shouldGenerateJobSummary()) { yield jobSummary.generateJobSummary(buildResults, cacheListener);
yield (0, job_summary_1.writeJobSummary)(buildResults, cacheListener);
}
else {
(0, job_summary_1.logJobSummary)(buildResults, cacheListener);
}
yield dependencyGraph.complete(params.getDependencyGraphOption()); yield dependencyGraph.complete(params.getDependencyGraphOption());
core.info('Completed post-action step'); core.info('Completed post-action step');
}); });
@ -141068,12 +141135,6 @@ function determineUserHome() {
return userHome; return userHome;
}); });
} }
function shouldGenerateJobSummary() {
if (!process.env[summary_1.SUMMARY_ENV_VAR]) {
return false;
}
return params.isJobSummaryEnabled();
}
/***/ }), /***/ }),

File diff suppressed because one or more lines are too long

223
dist/post/index.js vendored
View file

@ -136737,8 +136737,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.logCachingReport = exports.writeCachingReport = exports.CacheEntryListener = exports.CacheListener = void 0; exports.generateCachingReport = exports.CacheEntryListener = exports.CacheListener = void 0;
const core = __importStar(__nccwpck_require__(42186));
const cache = __importStar(__nccwpck_require__(27799)); const cache = __importStar(__nccwpck_require__(27799));
class CacheListener { class CacheListener {
constructor() { constructor() {
@ -136826,37 +136825,36 @@ class CacheEntryListener {
} }
} }
exports.CacheEntryListener = CacheEntryListener; exports.CacheEntryListener = CacheEntryListener;
function writeCachingReport(listener) { function generateCachingReport(listener) {
const entries = listener.cacheEntries; const entries = listener.cacheEntries;
core.summary.addRaw(`\n<details><summary><h4>Caching for gradle-build-action was ${listener.cacheStatus} - expand for details</h4></summary>\n`); return `
core.summary.addTable([ <details>
[ <summary><h4>Caching for gradle-build-action was ${listener.cacheStatus} - expand for details</h4></summary>
{ data: '', header: true }, ${renderEntryTable(entries)}
{ data: 'Count', header: true },
{ data: 'Total Size (Mb)', header: true } <h5>Cache Entry Details</h5>
], <pre>
['Entries Restored', `${getCount(entries, e => e.restoredSize)}`, `${getSize(entries, e => e.restoredSize)}`], ${renderEntryDetails(listener)}
['Entries Saved', `${getCount(entries, e => e.savedSize)}`, `${getSize(entries, e => e.savedSize)}`]
]);
core.summary.addHeading('Cache Entry Details', 5);
const entryDetails = renderEntryDetails(listener);
core.summary.addRaw(`<pre>
${entryDetails}
</pre> </pre>
</details> </details>
`); `;
} }
exports.writeCachingReport = writeCachingReport; exports.generateCachingReport = generateCachingReport;
function logCachingReport(listener) { function renderEntryTable(entries) {
const entries = listener.cacheEntries; return `
core.startGroup(`Caching for gradle-build-action was ${listener.cacheStatus} - expand for details`); <table>
core.info(`Entries Restored: ${getCount(entries, e => e.restoredSize)} (${getSize(entries, e => e.restoredSize)} Mb)`); <tr><td></td><th>Count</th><th>Total Size (Mb)</th></tr>
core.info(`Entries Saved : ${getCount(entries, e => e.savedSize)} (${getSize(entries, e => e.savedSize)} Mb)`); <tr><td>Entries Restored</td>
core.info(`Cache Entry Details`); <td>${getCount(entries, e => e.restoredSize)}</td>
core.info(renderEntryDetails(listener)); <td>${getSize(entries, e => e.restoredSize)}</td>
core.endGroup(); </tr>
<tr><td>Entries Saved</td>
<td>${getCount(entries, e => e.savedSize)}</td>
<td>${getSize(entries, e => e.savedSize)}</td>
</tr>
</table>
`;
} }
exports.logCachingReport = logCachingReport;
function renderEntryDetails(listener) { function renderEntryDetails(listener) {
return listener.cacheEntries return listener.cacheEntries
.map(entry => { .map(entry => {
@ -137665,7 +137663,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.DependencyGraphOption = exports.parseNumericInput = exports.getArtifactRetentionDays = exports.getDependencyGraphOption = exports.isDependencyGraphEnabled = exports.isJobSummaryEnabled = exports.getGithubToken = exports.getJobMatrix = exports.getArguments = exports.getGradleExecutable = exports.getGradleVersion = exports.getBuildRootDirectory = exports.getCacheExcludes = exports.getCacheIncludes = exports.getCacheEncryptionKey = exports.isCacheCleanupEnabled = exports.isCacheDebuggingEnabled = exports.isCacheStrictMatch = exports.isCacheOverwriteExisting = exports.isCacheWriteOnly = exports.isCacheReadOnly = exports.isCacheDisabled = void 0; exports.JobSummaryOption = exports.DependencyGraphOption = exports.parseNumericInput = exports.getArtifactRetentionDays = exports.getDependencyGraphOption = exports.getPRCommentOption = exports.getJobSummaryOption = exports.isJobSummaryEnabled = exports.getGithubToken = exports.getJobMatrix = exports.getArguments = exports.getGradleExecutable = exports.getGradleVersion = exports.getBuildRootDirectory = exports.getCacheExcludes = exports.getCacheIncludes = exports.getCacheEncryptionKey = exports.isCacheCleanupEnabled = exports.isCacheDebuggingEnabled = exports.isCacheStrictMatch = exports.isCacheOverwriteExisting = exports.isCacheWriteOnly = exports.isCacheReadOnly = exports.isCacheDisabled = void 0;
const core = __importStar(__nccwpck_require__(42186)); const core = __importStar(__nccwpck_require__(42186));
const string_argv_1 = __nccwpck_require__(19663); const string_argv_1 = __nccwpck_require__(19663);
function isCacheDisabled() { function isCacheDisabled() {
@ -137737,10 +137735,26 @@ function isJobSummaryEnabled() {
return getBooleanInput('generate-job-summary', true); return getBooleanInput('generate-job-summary', true);
} }
exports.isJobSummaryEnabled = isJobSummaryEnabled; exports.isJobSummaryEnabled = isJobSummaryEnabled;
function isDependencyGraphEnabled() { function getJobSummaryOption() {
return getBooleanInput('generate-dependency-graph', true); return parseJobSummaryOption('add-job-summary');
}
exports.getJobSummaryOption = getJobSummaryOption;
function getPRCommentOption() {
return parseJobSummaryOption('add-job-summary-as-pr-comment');
}
exports.getPRCommentOption = getPRCommentOption;
function parseJobSummaryOption(paramName) {
const val = core.getInput(paramName);
switch (val.toLowerCase().trim()) {
case 'never':
return JobSummaryOption.Never;
case 'always':
return JobSummaryOption.Always;
case 'on-failure':
return JobSummaryOption.OnFailure;
}
throw TypeError(`The value '${val}' is not valid for ${paramName}. Valid values are: [never, always, on-failure].`);
} }
exports.isDependencyGraphEnabled = isDependencyGraphEnabled;
function getDependencyGraphOption() { function getDependencyGraphOption() {
const val = core.getInput('dependency-graph'); const val = core.getInput('dependency-graph');
switch (val.toLowerCase().trim()) { switch (val.toLowerCase().trim()) {
@ -137755,7 +137769,7 @@ function getDependencyGraphOption() {
case 'download-and-submit': case 'download-and-submit':
return DependencyGraphOption.DownloadAndSubmit; return DependencyGraphOption.DownloadAndSubmit;
} }
throw TypeError(`The value '${val} is not valid for 'dependency-graph. Valid values are: [disabled, generate, generate-and-submit, generate-and-upload, download-and-submit]. The default value is 'disabled'.`); throw TypeError(`The value '${val}' is not valid for 'dependency-graph'. Valid values are: [disabled, generate, generate-and-submit, generate-and-upload, download-and-submit]. The default value is 'disabled'.`);
} }
exports.getDependencyGraphOption = getDependencyGraphOption; exports.getDependencyGraphOption = getDependencyGraphOption;
function getArtifactRetentionDays() { function getArtifactRetentionDays() {
@ -137794,6 +137808,12 @@ var DependencyGraphOption;
DependencyGraphOption["GenerateAndUpload"] = "generate-and-upload"; DependencyGraphOption["GenerateAndUpload"] = "generate-and-upload";
DependencyGraphOption["DownloadAndSubmit"] = "download-and-submit"; DependencyGraphOption["DownloadAndSubmit"] = "download-and-submit";
})(DependencyGraphOption || (exports.DependencyGraphOption = DependencyGraphOption = {})); })(DependencyGraphOption || (exports.DependencyGraphOption = DependencyGraphOption = {}));
var JobSummaryOption;
(function (JobSummaryOption) {
JobSummaryOption["Never"] = "never";
JobSummaryOption["Always"] = "always";
JobSummaryOption["OnFailure"] = "on-failure";
})(JobSummaryOption || (exports.JobSummaryOption = JobSummaryOption = {}));
/***/ }), /***/ }),
@ -137836,48 +137856,89 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.logJobSummary = exports.writeJobSummary = void 0; exports.generateJobSummary = void 0;
const core = __importStar(__nccwpck_require__(42186)); const core = __importStar(__nccwpck_require__(42186));
const github = __importStar(__nccwpck_require__(95438));
const summary_1 = __nccwpck_require__(81327);
const request_error_1 = __nccwpck_require__(10537);
const params = __importStar(__nccwpck_require__(23885));
const cache_reporting_1 = __nccwpck_require__(66674); const cache_reporting_1 = __nccwpck_require__(66674);
function writeJobSummary(buildResults, cacheListener) { function generateJobSummary(buildResults, cacheListener) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
core.info('Writing job summary'); const summaryTable = renderSummaryTable(buildResults);
if (buildResults.length === 0) { const cachingReport = (0, cache_reporting_1.generateCachingReport)(cacheListener);
core.debug('No Gradle build results found. Summary table will not be generated.'); if (shouldGenerateJobSummary(buildResults)) {
} core.info('Generating Job Summary');
else { core.summary.addRaw(summaryTable);
writeSummaryTable(buildResults); core.summary.addRaw(cachingReport);
}
(0, cache_reporting_1.writeCachingReport)(cacheListener);
yield core.summary.write(); yield core.summary.write();
});
}
exports.writeJobSummary = writeJobSummary;
function logJobSummary(buildResults, cacheListener) {
return __awaiter(this, void 0, void 0, function* () {
if (buildResults.length === 0) {
core.debug('No Gradle build results found. Summary table will not be logged.');
} }
else { else {
logSummaryTable(buildResults); core.info('============================');
core.info(summaryTable);
core.info('============================');
core.info(cachingReport);
core.info('============================');
}
if (shouldAddPRComment(buildResults)) {
yield addPRComment(summaryTable);
} }
(0, cache_reporting_1.logCachingReport)(cacheListener);
}); });
} }
exports.logJobSummary = logJobSummary; exports.generateJobSummary = generateJobSummary;
function writeSummaryTable(results) { function addPRComment(jobSummary) {
core.summary.addHeading('Gradle Builds', 3); return __awaiter(this, void 0, void 0, function* () {
core.summary.addRaw(` const context = github.context;
if (context.payload.pull_request == null) {
core.info('No pull_request trigger: not adding PR comment');
return;
}
const pull_request_number = context.payload.pull_request.number;
core.info(`Adding Job Summary as comment to PR #${pull_request_number}.`);
const prComment = `<h3>Job Summary for gradle-build-action</h3>
<h5>${github.context.workflow} :: <em>${github.context.job}</em></h5>
${jobSummary}`;
const github_token = params.getGithubToken();
const octokit = github.getOctokit(github_token);
try {
yield octokit.rest.issues.createComment(Object.assign(Object.assign({}, context.repo), { issue_number: pull_request_number, body: prComment }));
}
catch (error) {
if (error instanceof request_error_1.RequestError) {
core.warning(buildWarningMessage(error));
}
else {
throw error;
}
}
});
}
function buildWarningMessage(error) {
const mainWarning = `Failed to generate PR comment.\n${String(error)}`;
if (error.message === 'Resource not accessible by integration') {
return `${mainWarning}
Please ensure that the 'pull-requests: write' permission is available for the workflow job.
Note that this permission is never available for a workflow triggered from a repository fork.
`;
}
return mainWarning;
}
function renderSummaryTable(results) {
if (results.length === 0) {
return 'No Gradle build results detected.';
}
return `
<table> <table>
<tr> <tr>
<th>Root Project</th> <th>Gradle Root Project</th>
<th>Requested Tasks</th> <th>Requested Tasks</th>
<th>Gradle Version</th> <th>Gradle Version</th>
<th>Build Outcome</th> <th>Build Outcome</th>
<th>Build Scan®</th> <th>Build Scan®</th>
</tr>${results.map(result => renderBuildResultRow(result)).join('')} </tr>${results.map(result => renderBuildResultRow(result)).join('')}
</table> </table>
`); `;
} }
function renderBuildResultRow(result) { function renderBuildResultRow(result) {
return ` return `
@ -137906,16 +137967,28 @@ function renderBuildScanBadge(outcomeText, outcomeColor, targetUrl) {
const badgeHtml = `<img src="${badgeUrl}" alt="Build Scan ${outcomeText}" />`; const badgeHtml = `<img src="${badgeUrl}" alt="Build Scan ${outcomeText}" />`;
return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`; return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`;
} }
function logSummaryTable(results) { function shouldGenerateJobSummary(buildResults) {
core.info('============================'); if (!process.env[summary_1.SUMMARY_ENV_VAR]) {
core.info('Gradle Builds'); return false;
core.info('----------------------------'); }
core.info('Root Project | Requested Tasks | Gradle Version | Build Outcome | Build Scan®'); if (!params.isJobSummaryEnabled()) {
core.info('----------------------------'); return false;
for (const result of results) { }
core.info(`${result.rootProjectName} | ${result.requestedTasks} | ${result.gradleVersion} | ${result.buildFailed ? 'FAILED' : 'SUCCESS'} | ${result.buildScanFailed ? 'Publish failed' : result.buildScanUri}`); return shouldAddJobSummary(params.getJobSummaryOption(), buildResults);
}
function shouldAddPRComment(buildResults) {
return shouldAddJobSummary(params.getPRCommentOption(), buildResults);
}
function shouldAddJobSummary(option, buildResults) {
switch (option) {
case params.JobSummaryOption.Always:
return true;
case params.JobSummaryOption.Never:
return false;
case params.JobSummaryOption.OnFailure:
core.info(`Got these build results: ${JSON.stringify(buildResults)}`);
return buildResults.some(result => result.buildFailed);
} }
core.info('============================');
} }
@ -138075,14 +138148,13 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.complete = exports.setup = void 0; exports.complete = exports.setup = void 0;
const core = __importStar(__nccwpck_require__(42186)); const core = __importStar(__nccwpck_require__(42186));
const exec = __importStar(__nccwpck_require__(71514)); const exec = __importStar(__nccwpck_require__(71514));
const summary_1 = __nccwpck_require__(81327);
const path = __importStar(__nccwpck_require__(71017)); const path = __importStar(__nccwpck_require__(71017));
const os = __importStar(__nccwpck_require__(22037)); const os = __importStar(__nccwpck_require__(22037));
const caches = __importStar(__nccwpck_require__(23800)); const caches = __importStar(__nccwpck_require__(23800));
const layout = __importStar(__nccwpck_require__(28182)); const layout = __importStar(__nccwpck_require__(28182));
const params = __importStar(__nccwpck_require__(23885)); const params = __importStar(__nccwpck_require__(23885));
const dependencyGraph = __importStar(__nccwpck_require__(80)); const dependencyGraph = __importStar(__nccwpck_require__(80));
const job_summary_1 = __nccwpck_require__(87345); const jobSummary = __importStar(__nccwpck_require__(87345));
const build_results_1 = __nccwpck_require__(82107); const build_results_1 = __nccwpck_require__(82107);
const cache_reporting_1 = __nccwpck_require__(66674); const cache_reporting_1 = __nccwpck_require__(66674);
const daemon_controller_1 = __nccwpck_require__(85146); const daemon_controller_1 = __nccwpck_require__(85146);
@ -138118,12 +138190,7 @@ function complete() {
const cacheListener = cache_reporting_1.CacheListener.rehydrate(core.getState(CACHE_LISTENER)); const cacheListener = cache_reporting_1.CacheListener.rehydrate(core.getState(CACHE_LISTENER));
const daemonController = new daemon_controller_1.DaemonController(buildResults); const daemonController = new daemon_controller_1.DaemonController(buildResults);
yield caches.save(gradleUserHome, cacheListener, daemonController); yield caches.save(gradleUserHome, cacheListener, daemonController);
if (shouldGenerateJobSummary()) { yield jobSummary.generateJobSummary(buildResults, cacheListener);
yield (0, job_summary_1.writeJobSummary)(buildResults, cacheListener);
}
else {
(0, job_summary_1.logJobSummary)(buildResults, cacheListener);
}
yield dependencyGraph.complete(params.getDependencyGraphOption()); yield dependencyGraph.complete(params.getDependencyGraphOption());
core.info('Completed post-action step'); core.info('Completed post-action step');
}); });
@ -138153,12 +138220,6 @@ function determineUserHome() {
return userHome; return userHome;
}); });
} }
function shouldGenerateJobSummary() {
if (!process.env[summary_1.SUMMARY_ENV_VAR]) {
return false;
}
return params.isJobSummaryEnabled();
}
/***/ }), /***/ }),

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,3 @@
import * as core from '@actions/core'
import * as cache from '@actions/cache' import * as cache from '@actions/cache'
/** /**
@ -112,47 +111,36 @@ export class CacheEntryListener {
} }
} }
export function writeCachingReport(listener: CacheListener): void { export function generateCachingReport(listener: CacheListener): string {
const entries = listener.cacheEntries const entries = listener.cacheEntries
core.summary.addRaw( return `
`\n<details><summary><h4>Caching for gradle-build-action was ${listener.cacheStatus} - expand for details</h4></summary>\n` <details>
) <summary><h4>Caching for gradle-build-action was ${listener.cacheStatus} - expand for details</h4></summary>
${renderEntryTable(entries)}
core.summary.addTable([ <h5>Cache Entry Details</h5>
[ <pre>
{data: '', header: true}, ${renderEntryDetails(listener)}
{data: 'Count', header: true},
{data: 'Total Size (Mb)', header: true}
],
['Entries Restored', `${getCount(entries, e => e.restoredSize)}`, `${getSize(entries, e => e.restoredSize)}`],
['Entries Saved', `${getCount(entries, e => e.savedSize)}`, `${getSize(entries, e => e.savedSize)}`]
])
core.summary.addHeading('Cache Entry Details', 5)
const entryDetails = renderEntryDetails(listener)
core.summary.addRaw(`<pre>
${entryDetails}
</pre> </pre>
</details> </details>
`) `
} }
export function logCachingReport(listener: CacheListener): void { function renderEntryTable(entries: CacheEntryListener[]): string {
const entries = listener.cacheEntries return `
<table>
core.startGroup(`Caching for gradle-build-action was ${listener.cacheStatus} - expand for details`) <tr><td></td><th>Count</th><th>Total Size (Mb)</th></tr>
<tr><td>Entries Restored</td>
core.info( <td>${getCount(entries, e => e.restoredSize)}</td>
`Entries Restored: ${getCount(entries, e => e.restoredSize)} (${getSize(entries, e => e.restoredSize)} Mb)` <td>${getSize(entries, e => e.restoredSize)}</td>
) </tr>
core.info(`Entries Saved : ${getCount(entries, e => e.savedSize)} (${getSize(entries, e => e.savedSize)} Mb)`) <tr><td>Entries Saved</td>
<td>${getCount(entries, e => e.savedSize)}</td>
core.info(`Cache Entry Details`) <td>${getSize(entries, e => e.savedSize)}</td>
core.info(renderEntryDetails(listener)) </tr>
</table>
core.endGroup() `
} }
function renderEntryDetails(listener: CacheListener): string { function renderEntryDetails(listener: CacheListener): string {

View file

@ -71,8 +71,25 @@ export function isJobSummaryEnabled(): boolean {
return getBooleanInput('generate-job-summary', true) return getBooleanInput('generate-job-summary', true)
} }
export function isDependencyGraphEnabled(): boolean { export function getJobSummaryOption(): JobSummaryOption {
return getBooleanInput('generate-dependency-graph', true) return parseJobSummaryOption('add-job-summary')
}
export function getPRCommentOption(): JobSummaryOption {
return parseJobSummaryOption('add-job-summary-as-pr-comment')
}
function parseJobSummaryOption(paramName: string): JobSummaryOption {
const val = core.getInput(paramName)
switch (val.toLowerCase().trim()) {
case 'never':
return JobSummaryOption.Never
case 'always':
return JobSummaryOption.Always
case 'on-failure':
return JobSummaryOption.OnFailure
}
throw TypeError(`The value '${val}' is not valid for ${paramName}. Valid values are: [never, always, on-failure].`)
} }
export function getDependencyGraphOption(): DependencyGraphOption { export function getDependencyGraphOption(): DependencyGraphOption {
@ -90,7 +107,7 @@ export function getDependencyGraphOption(): DependencyGraphOption {
return DependencyGraphOption.DownloadAndSubmit return DependencyGraphOption.DownloadAndSubmit
} }
throw TypeError( throw TypeError(
`The value '${val} is not valid for 'dependency-graph. Valid values are: [disabled, generate, generate-and-submit, generate-and-upload, download-and-submit]. The default value is 'disabled'.` `The value '${val}' is not valid for 'dependency-graph'. Valid values are: [disabled, generate, generate-and-submit, generate-and-upload, download-and-submit]. The default value is 'disabled'.`
) )
} }
@ -131,3 +148,9 @@ export enum DependencyGraphOption {
GenerateAndUpload = 'generate-and-upload', GenerateAndUpload = 'generate-and-upload',
DownloadAndSubmit = 'download-and-submit' DownloadAndSubmit = 'download-and-submit'
} }
export enum JobSummaryOption {
Never = 'never',
Always = 'always',
OnFailure = 'on-failure'
}

View file

@ -1,45 +1,94 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as github from '@actions/github'
import {SUMMARY_ENV_VAR} from '@actions/core/lib/summary'
import {RequestError} from '@octokit/request-error'
import * as params from './input-params'
import {BuildResult} from './build-results' import {BuildResult} from './build-results'
import {writeCachingReport, CacheListener, logCachingReport} from './cache-reporting' import {CacheListener, generateCachingReport} from './cache-reporting'
export async function writeJobSummary(buildResults: BuildResult[], cacheListener: CacheListener): Promise<void> { export async function generateJobSummary(buildResults: BuildResult[], cacheListener: CacheListener): Promise<void> {
core.info('Writing job summary') const summaryTable = renderSummaryTable(buildResults)
const cachingReport = generateCachingReport(cacheListener)
if (buildResults.length === 0) { if (shouldGenerateJobSummary(buildResults)) {
core.debug('No Gradle build results found. Summary table will not be generated.') core.info('Generating Job Summary')
} else {
writeSummaryTable(buildResults)
}
writeCachingReport(cacheListener)
core.summary.addRaw(summaryTable)
core.summary.addRaw(cachingReport)
await core.summary.write() await core.summary.write()
}
export async function logJobSummary(buildResults: BuildResult[], cacheListener: CacheListener): Promise<void> {
if (buildResults.length === 0) {
core.debug('No Gradle build results found. Summary table will not be logged.')
} else { } else {
logSummaryTable(buildResults) core.info('============================')
core.info(summaryTable)
core.info('============================')
core.info(cachingReport)
core.info('============================')
} }
logCachingReport(cacheListener) if (shouldAddPRComment(buildResults)) {
await addPRComment(summaryTable)
}
} }
function writeSummaryTable(results: BuildResult[]): void { async function addPRComment(jobSummary: string): Promise<void> {
core.summary.addHeading('Gradle Builds', 3) const context = github.context
if (context.payload.pull_request == null) {
core.info('No pull_request trigger: not adding PR comment')
return
}
core.summary.addRaw(` const pull_request_number = context.payload.pull_request.number
core.info(`Adding Job Summary as comment to PR #${pull_request_number}.`)
const prComment = `<h3>Job Summary for gradle-build-action</h3>
<h5>${github.context.workflow} :: <em>${github.context.job}</em></h5>
${jobSummary}`
const github_token = params.getGithubToken()
const octokit = github.getOctokit(github_token)
try {
await octokit.rest.issues.createComment({
...context.repo,
issue_number: pull_request_number,
body: prComment
})
} catch (error) {
if (error instanceof RequestError) {
core.warning(buildWarningMessage(error))
} else {
throw error
}
}
}
function buildWarningMessage(error: RequestError): string {
const mainWarning = `Failed to generate PR comment.\n${String(error)}`
if (error.message === 'Resource not accessible by integration') {
return `${mainWarning}
Please ensure that the 'pull-requests: write' permission is available for the workflow job.
Note that this permission is never available for a workflow triggered from a repository fork.
`
}
return mainWarning
}
function renderSummaryTable(results: BuildResult[]): string {
if (results.length === 0) {
return 'No Gradle build results detected.'
}
return `
<table> <table>
<tr> <tr>
<th>Root Project</th> <th>Gradle Root Project</th>
<th>Requested Tasks</th> <th>Requested Tasks</th>
<th>Gradle Version</th> <th>Gradle Version</th>
<th>Build Outcome</th> <th>Build Outcome</th>
<th>Build Scan®</th> <th>Build Scan®</th>
</tr>${results.map(result => renderBuildResultRow(result)).join('')} </tr>${results.map(result => renderBuildResultRow(result)).join('')}
</table> </table>
`) `
} }
function renderBuildResultRow(result: BuildResult): string { function renderBuildResultRow(result: BuildResult): string {
@ -77,18 +126,32 @@ function renderBuildScanBadge(outcomeText: string, outcomeColor: string, targetU
return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>` return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`
} }
function logSummaryTable(results: BuildResult[]): void { function shouldGenerateJobSummary(buildResults: BuildResult[]): boolean {
core.info('============================') // Check if Job Summary is supported on this platform
core.info('Gradle Builds') if (!process.env[SUMMARY_ENV_VAR]) {
core.info('----------------------------') return false
core.info('Root Project | Requested Tasks | Gradle Version | Build Outcome | Build Scan®') }
core.info('----------------------------')
for (const result of results) { // Check if Job Summary is disabled using the deprecated input
core.info( if (!params.isJobSummaryEnabled()) {
`${result.rootProjectName} | ${result.requestedTasks} | ${result.gradleVersion} | ${ return false
result.buildFailed ? 'FAILED' : 'SUCCESS' }
} | ${result.buildScanFailed ? 'Publish failed' : result.buildScanUri}`
) return shouldAddJobSummary(params.getJobSummaryOption(), buildResults)
}
function shouldAddPRComment(buildResults: BuildResult[]): boolean {
return shouldAddJobSummary(params.getPRCommentOption(), buildResults)
}
function shouldAddJobSummary(option: params.JobSummaryOption, buildResults: BuildResult[]): boolean {
switch (option) {
case params.JobSummaryOption.Always:
return true
case params.JobSummaryOption.Never:
return false
case params.JobSummaryOption.OnFailure:
core.info(`Got these build results: ${JSON.stringify(buildResults)}`)
return buildResults.some(result => result.buildFailed)
} }
core.info('============================')
} }

View file

@ -1,14 +1,13 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as exec from '@actions/exec' import * as exec from '@actions/exec'
import {SUMMARY_ENV_VAR} from '@actions/core/lib/summary'
import * as path from 'path' import * as path from 'path'
import * as os from 'os' import * as os from 'os'
import * as caches from './caches' import * as caches from './caches'
import * as layout from './repository-layout' import * as layout from './repository-layout'
import * as params from './input-params' import * as params from './input-params'
import * as dependencyGraph from './dependency-graph' import * as dependencyGraph from './dependency-graph'
import * as jobSummary from './job-summary'
import {logJobSummary, writeJobSummary} from './job-summary'
import {loadBuildResults} from './build-results' import {loadBuildResults} from './build-results'
import {CacheListener} from './cache-reporting' import {CacheListener} from './cache-reporting'
import {DaemonController} from './daemon-controller' import {DaemonController} from './daemon-controller'
@ -56,11 +55,7 @@ export async function complete(): Promise<void> {
await caches.save(gradleUserHome, cacheListener, daemonController) await caches.save(gradleUserHome, cacheListener, daemonController)
if (shouldGenerateJobSummary()) { await jobSummary.generateJobSummary(buildResults, cacheListener)
await writeJobSummary(buildResults, cacheListener)
} else {
logJobSummary(buildResults, cacheListener)
}
await dependencyGraph.complete(params.getDependencyGraphOption()) await dependencyGraph.complete(params.getDependencyGraphOption())
@ -93,12 +88,3 @@ async function determineUserHome(): Promise<string> {
core.debug(`Determined user.home from java -version output: '${userHome}'`) core.debug(`Determined user.home from java -version output: '${userHome}'`)
return userHome return userHome
} }
function shouldGenerateJobSummary(): boolean {
// Check if Job Summary is supported on this platform
if (!process.env[SUMMARY_ENV_VAR]) {
return false
}
return params.isJobSummaryEnabled()
}