Skip to content

Commit

Permalink
Merge pull request #333 from gradle/dd/build-scan-failure
Browse files Browse the repository at this point in the history
Report failure to publish build scan in Job Summary
  • Loading branch information
bigdaz authored Jun 19, 2022
2 parents 2335d51 + ce3874f commit 67421db
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 106 deletions.
1 change: 1 addition & 0 deletions .github/workflow-samples/groovy-dsl/settings.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id "com.gradle.enterprise" version "3.10.2"
id "com.gradle.common-custom-user-data-gradle-plugin" version "1.7.2"
}

gradleEnterprise {
Expand Down
1 change: 1 addition & 0 deletions .github/workflow-samples/kotlin-dsl/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id("com.gradle.enterprise") version "3.10.2"
id("com.gradle.common-custom-user-data-gradle-plugin") version "1.7.2"
}

gradleEnterprise {
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/demo-job-summary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ jobs:
run: ./gradlew assemble
- name: Build kotlin-dsl project without build scan
working-directory: .github/workflow-samples/kotlin-dsl
run: ./gradlew check --no-scan
run: ./gradlew assemble check --no-scan
- name: Build kotlin-dsl project with build scan publish failure
working-directory: .github/workflow-samples/kotlin-dsl
run: ./gradlew check -Dgradle.enterprise.url=https://not.valid.server
- name: Build groovy-dsl project
working-directory: .github/workflow-samples/groovy-dsl
run: ./gradlew assemble
Expand Down
55 changes: 35 additions & 20 deletions dist/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66034,28 +66034,43 @@ function loadBuildResults() {
exports.loadBuildResults = loadBuildResults;
function writeSummaryTable(results) {
core.summary.addHeading('Gradle Builds', 3);
core.summary.addTable([
[
{ data: 'Root Project', header: true },
{ data: 'Tasks', header: true },
{ data: 'Gradle Version', header: true },
{ data: 'Outcome', header: true }
],
...results.map(result => [
result.rootProjectName,
result.requestedTasks,
result.gradleVersion,
renderOutcome(result)
])
]);
core.summary.addRaw('\n');
core.summary.addRaw(`
<table>
<tr>
<th>Root Project</th>
<th>Requested Tasks</th>
<th>Gradle Version</th>
<th>Build Outcome</th>
<th>Build Scan™</th>
</tr>${results.map(result => renderBuildResultRow(result)).join('')}
</table>
`);
}
function renderBuildResultRow(result) {
return `
<tr>
<td>${result.rootProjectName}</td>
<td>${result.requestedTasks}</td>
<td align='center'>${result.gradleVersion}</td>
<td align='center'>${renderOutcome(result)}</td>
<td>${renderBuildScan(result)}</td>
</tr>`;
}
function renderOutcome(result) {
const labelPart = result.buildScanUri ? 'Build%20Scan%E2%84%A2' : 'Build';
const outcomePart = result.buildFailed ? 'FAILED-red' : 'SUCCESS-brightgreen';
const badgeUrl = `https://img.shields.io/badge/${labelPart}-${outcomePart}?logo=Gradle`;
const badgeHtml = `<img src="${badgeUrl}" alt="Gradle Build">`;
const targetUrl = result.buildScanUri ? result.buildScanUri : '#';
return result.buildFailed ? ':x:' : ':white_check_mark:';
}
function renderBuildScan(result) {
if (result.buildScanFailed) {
return renderBuildScanBadge('PUBLISH_FAILED', 'orange', 'https://docs.gradle.com/enterprise/gradle-plugin/#troubleshooting');
}
if (result.buildScanUri) {
return renderBuildScanBadge('PUBLISHED', '06A0CE', result.buildScanUri);
}
return renderBuildScanBadge('NOT_PUBLISHED', 'lightgrey', 'https://scans.gradle.com');
}
function renderBuildScanBadge(outcomeText, outcomeColor, targetUrl) {
const badgeUrl = `https://img.shields.io/badge/Build%20Scan%E2%84%A2-${outcomeText}-${outcomeColor}?logo=Gradle`;
const badgeHtml = `<img src="${badgeUrl}" alt="Build Scan ${outcomeText}" />`;
return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`;
}

Expand Down
2 changes: 1 addition & 1 deletion dist/main/index.js.map

Large diffs are not rendered by default.

55 changes: 35 additions & 20 deletions dist/post/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -64954,28 +64954,43 @@ function loadBuildResults() {
exports.loadBuildResults = loadBuildResults;
function writeSummaryTable(results) {
core.summary.addHeading('Gradle Builds', 3);
core.summary.addTable([
[
{ data: 'Root Project', header: true },
{ data: 'Tasks', header: true },
{ data: 'Gradle Version', header: true },
{ data: 'Outcome', header: true }
],
...results.map(result => [
result.rootProjectName,
result.requestedTasks,
result.gradleVersion,
renderOutcome(result)
])
]);
core.summary.addRaw('\n');
core.summary.addRaw(`
<table>
<tr>
<th>Root Project</th>
<th>Requested Tasks</th>
<th>Gradle Version</th>
<th>Build Outcome</th>
<th>Build Scan™</th>
</tr>${results.map(result => renderBuildResultRow(result)).join('')}
</table>
`);
}
function renderBuildResultRow(result) {
return `
<tr>
<td>${result.rootProjectName}</td>
<td>${result.requestedTasks}</td>
<td align='center'>${result.gradleVersion}</td>
<td align='center'>${renderOutcome(result)}</td>
<td>${renderBuildScan(result)}</td>
</tr>`;
}
function renderOutcome(result) {
const labelPart = result.buildScanUri ? 'Build%20Scan%E2%84%A2' : 'Build';
const outcomePart = result.buildFailed ? 'FAILED-red' : 'SUCCESS-brightgreen';
const badgeUrl = `https://img.shields.io/badge/${labelPart}-${outcomePart}?logo=Gradle`;
const badgeHtml = `<img src="${badgeUrl}" alt="Gradle Build">`;
const targetUrl = result.buildScanUri ? result.buildScanUri : '#';
return result.buildFailed ? ':x:' : ':white_check_mark:';
}
function renderBuildScan(result) {
if (result.buildScanFailed) {
return renderBuildScanBadge('PUBLISH_FAILED', 'orange', 'https://docs.gradle.com/enterprise/gradle-plugin/#troubleshooting');
}
if (result.buildScanUri) {
return renderBuildScanBadge('PUBLISHED', '06A0CE', result.buildScanUri);
}
return renderBuildScanBadge('NOT_PUBLISHED', 'lightgrey', 'https://scans.gradle.com');
}
function renderBuildScanBadge(outcomeText, outcomeColor, targetUrl) {
const badgeUrl = `https://img.shields.io/badge/Build%20Scan%E2%84%A2-${outcomeText}-${outcomeColor}?logo=Gradle`;
const badgeHtml = `<img src="${badgeUrl}" alt="Build Scan ${outcomeText}" />`;
return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`;
}

Expand Down
2 changes: 1 addition & 1 deletion dist/post/index.js.map

Large diffs are not rendered by default.

64 changes: 44 additions & 20 deletions src/job-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface BuildResult {
get gradleHomeDir(): string
get buildFailed(): boolean
get buildScanUri(): string
get buildScanFailed(): boolean
}

export async function writeJobSummary(buildResults: BuildResult[], cacheListener: CacheListener): Promise<void> {
Expand Down Expand Up @@ -43,28 +44,51 @@ export function loadBuildResults(): BuildResult[] {

function writeSummaryTable(results: BuildResult[]): void {
core.summary.addHeading('Gradle Builds', 3)
core.summary.addTable([
[
{data: 'Root Project', header: true},
{data: 'Tasks', header: true},
{data: 'Gradle Version', header: true},
{data: 'Outcome', header: true}
],
...results.map(result => [
result.rootProjectName,
result.requestedTasks,
result.gradleVersion,
renderOutcome(result)
])
])
core.summary.addRaw('\n')

core.summary.addRaw(`
<table>
<tr>
<th>Root Project</th>
<th>Requested Tasks</th>
<th>Gradle Version</th>
<th>Build Outcome</th>
<th>Build Scan™</th>
</tr>${results.map(result => renderBuildResultRow(result)).join('')}
</table>
`)
}

function renderBuildResultRow(result: BuildResult): string {
return `
<tr>
<td>${result.rootProjectName}</td>
<td>${result.requestedTasks}</td>
<td align='center'>${result.gradleVersion}</td>
<td align='center'>${renderOutcome(result)}</td>
<td>${renderBuildScan(result)}</td>
</tr>`
}

function renderOutcome(result: BuildResult): string {
const labelPart = result.buildScanUri ? 'Build%20Scan%E2%84%A2' : 'Build'
const outcomePart = result.buildFailed ? 'FAILED-red' : 'SUCCESS-brightgreen'
const badgeUrl = `https://img.shields.io/badge/${labelPart}-${outcomePart}?logo=Gradle`
const badgeHtml = `<img src="${badgeUrl}" alt="Gradle Build">`
const targetUrl = result.buildScanUri ? result.buildScanUri : '#'
return result.buildFailed ? ':x:' : ':white_check_mark:'
}

function renderBuildScan(result: BuildResult): string {
if (result.buildScanFailed) {
return renderBuildScanBadge(
'PUBLISH_FAILED',
'orange',
'https://docs.gradle.com/enterprise/gradle-plugin/#troubleshooting'
)
}
if (result.buildScanUri) {
return renderBuildScanBadge('PUBLISHED', '06A0CE', result.buildScanUri)
}
return renderBuildScanBadge('NOT_PUBLISHED', 'lightgrey', 'https://scans.gradle.com')
}

function renderBuildScanBadge(outcomeText: string, outcomeColor: string, targetUrl: string): string {
const badgeUrl = `https://img.shields.io/badge/Build%20Scan%E2%84%A2-${outcomeText}-${outcomeColor}?logo=Gradle`
const badgeHtml = `<img src="${badgeUrl}" alt="Build Scan ${outcomeText}" />`
return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ abstract class BuildResultsRecorder implements BuildService<BuildResultsRecorder
gradleVersion: GradleVersion.current().version,
gradleHomeDir: getParameters().getGradleHomeDir().get(),
buildFailed: buildFailed,
buildScanUri: null
buildScanUri: null,
buildScanFailed: false
]

def buildResultsDir = new File(System.getenv("RUNNER_TEMP"), ".build-results")
Expand Down
96 changes: 55 additions & 41 deletions src/resources/init-scripts/build-result-capture.init.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,69 +40,83 @@ if (isTopLevelBuild) {

def captureUsingBuildScanPublished(buildScanExtension, rootProject, invocationId) {
buildScanExtension.with {
def requestedTasks = gradle.startParameter.taskNames.join(" ")
def rootProjectName = rootProject.name
def rootProjectDir = rootProject.projectDir.absolutePath
def gradleVersion = GradleVersion.current().version
def gradleHomeDir = gradle.gradleHomeDir.absolutePath
def buildFailed = false
def buildResults = new BuildResults(invocationId, gradle, rootProject)

buildFinished { result ->
buildFailed = (result.failure != null)
buildResults.setBuildResult(result)
}

buildScanPublished { buildScan ->

def buildScanUri = buildScan.buildScanUri.toASCIIString()
def buildResults = [
rootProjectName: rootProjectName,
rootProjectDir: rootProjectDir,
requestedTasks: requestedTasks,
gradleVersion: gradleVersion,
gradleHomeDir: gradleHomeDir,
buildFailed: buildFailed,
buildScanUri: buildScanUri
]

def buildResultsDir = new File(System.getenv("RUNNER_TEMP"), ".build-results")
buildResultsDir.mkdirs()
def buildResultsFile = new File(buildResultsDir, System.getenv("GITHUB_ACTION") + invocationId + ".json")

// Overwrite any contents written by buildFinished or build service, since this result is a superset.
if (buildResultsFile.exists()) {
buildResultsFile.text = groovy.json.JsonOutput.toJson(buildResults)
} else {
buildResultsFile << groovy.json.JsonOutput.toJson(buildResults)
}
buildResults.setBuildScanUri(buildScan.buildScanUri.toASCIIString())
buildResults.writeToResultsFile(true)

println("::set-output name=build-scan-url::${buildScan.buildScanUri}")
}

onError { error ->
buildResults.setBuildScanFailed()
buildResults.writeToResultsFile(true)
}
}
}

def captureUsingBuildFinished(gradle, invocationId) {
gradle.buildFinished { result ->
def buildResults = [
rootProjectName: gradle.rootProject.name,
rootProjectDir: gradle.rootProject.rootDir.absolutePath,
def buildResults = new BuildResults(invocationId, gradle, gradle.rootProject)
buildResults.setBuildResult(result)

buildResults.writeToResultsFile(false)

}
}

def captureUsingBuildService(settings, invocationId) {
gradle.ext.invocationId = invocationId
apply from: 'build-result-capture-service.plugin.groovy'
}

class BuildResults {
def invocationId
def buildResults

BuildResults(String invocationId, def gradle, def rootProject) {
this.invocationId = invocationId
buildResults = [
rootProjectName: rootProject.name,
rootProjectDir: rootProject.projectDir.absolutePath,
requestedTasks: gradle.startParameter.taskNames.join(" "),
gradleVersion: GradleVersion.current().version,
gradleHomeDir: gradle.gradleHomeDir.absolutePath,
buildFailed: result.failure != null,
buildScanUri: null
buildFailed: false,
buildScanUri: null,
buildScanFailed: false
]
}

def setBuildResult(def result) {
buildResults['buildFailed'] = result.failure != null
}

def setBuildScanUri(def buildScanUrl) {
buildResults['buildScanUri'] = buildScanUrl
}

def setBuildScanFailed() {
buildResults['buildScanFailed'] = true
}

def writeToResultsFile(boolean overwrite) {
def buildResultsDir = new File(System.getenv("RUNNER_TEMP"), ".build-results")
buildResultsDir.mkdirs()
def buildResultsFile = new File(buildResultsDir, System.getenv("GITHUB_ACTION") + invocationId + ".json")
// Don't overwrite file generated by build-scan plugin if present (which has build-scan-uri)
if (!buildResultsFile.exists()) {

// Overwrite any contents written by buildFinished or build service, since this result is a superset.
if (buildResultsFile.exists()) {
if (overwrite) {
buildResultsFile.text = groovy.json.JsonOutput.toJson(buildResults)
}
} else {
buildResultsFile << groovy.json.JsonOutput.toJson(buildResults)
}
}
}

def captureUsingBuildService(settings, invocationId) {
gradle.ext.invocationId = invocationId
apply from: 'build-result-capture-service.plugin.groovy'
}
Loading

0 comments on commit 67421db

Please sign in to comment.