Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve init scripts and add test coverage #304

Merged
merged 10 commits into from Jun 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/ci-init-script-check.yml
@@ -0,0 +1,23 @@
name: CI-init-script-check

on:
push:

jobs:
test-init-scripts:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 8
- name: Setup Gradle
uses: ./
with:
cache-read-only: false # For testing, allow writing cache entries on non-default branches
- name: Run integration tests
working-directory: test/test-init-scripts
run: ./gradlew check
5 changes: 4 additions & 1 deletion .github/workflows/demo-job-summary.yml
Expand Up @@ -23,10 +23,13 @@ jobs:
- 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 check --no-scan
- name: Build groovy-dsl project
working-directory: .github/workflow-samples/groovy-dsl
run: ./gradlew assemble
- name: Build kotlin-dsl project again
- name: Build kotlin-dsl project with multiple gradle invocations
working-directory: .github/workflow-samples/kotlin-dsl
run: |
./gradlew tasks --no-daemon
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/integ-test-execution.yml
Expand Up @@ -56,19 +56,26 @@ jobs:
gradle-versions:
strategy:
matrix:
gradle: [7.3, 6.9, 5.6.4, 4.10.3]
gradle: [7.3, 6.9, 5.6.4, 4.10.3, 3.5.1]
os: ${{fromJSON(inputs.runner-os)}}
include:
- gradle: 5.6.4
build-root-suffix: -gradle-5
- gradle: 4.10.3
build-root-suffix: -gradle-4
- gradle: 3.5.1
build-root-suffix: -gradle-4
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Download distribution if required
uses: ./.github/actions/download-dist
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 8
- name: Run Gradle build
uses: ./
id: gradle
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/integ-test-provision-gradle-versions.yml
Expand Up @@ -59,19 +59,26 @@ jobs:
gradle-versions:
strategy:
matrix:
gradle: [7.3, 6.9, 5.6.4, 4.10.3]
gradle: [7.3, 6.9, 5.6.4, 4.10.3, 3.5.1]
os: ${{fromJSON(inputs.runner-os)}}
include:
- gradle: 5.6.4
build-root-suffix: -gradle-5
- gradle: 4.10.3
build-root-suffix: -gradle-4
- gradle: 3.5.1
build-root-suffix: -gradle-4
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Download distribution if required
uses: ./.github/actions/download-dist
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 8
- name: Setup Gradle
uses: ./
with:
Expand Down
17 changes: 8 additions & 9 deletions dist/main/index.js
Expand Up @@ -64967,6 +64967,7 @@ class GradleStateCache {
fs_1.default.appendFileSync(propertiesFile, 'org.gradle.daemon=false');
const initScriptFilenames = [
'build-result-capture.init.gradle',
'build-result-capture-service.plugin.groovy',
'project-root-capture.init.gradle',
'project-root-capture.plugin.groovy'
];
Expand Down Expand Up @@ -65291,7 +65292,7 @@ class ConfigurationCacheEntryExtractor extends AbstractEntryExtractor {
});
}
getProjectRoots() {
const projectList = path_1.default.resolve(this.gradleUserHome, cache_base_1.PROJECT_ROOTS_FILE);
const projectList = path_1.default.resolve(process.env['RUNNER_TEMP'], cache_base_1.PROJECT_ROOTS_FILE);
if (!fs_1.default.existsSync(projectList)) {
core.info(`Missing project list file ${projectList}`);
return [];
Expand Down Expand Up @@ -65559,7 +65560,7 @@ function isCacheDisabled() {
}
exports.isCacheDisabled = isCacheDisabled;
function isCacheReadOnly() {
return core.getBooleanInput(CACHE_READONLY_PARAMETER);
return !isCacheWriteOnly() && core.getBooleanInput(CACHE_READONLY_PARAMETER);
}
exports.isCacheReadOnly = isCacheReadOnly;
function isCacheWriteOnly() {
Expand Down Expand Up @@ -66031,14 +66032,12 @@ function writeSummaryTable(results) {
core.summary.addRaw('\n');
}
function renderOutcome(result) {
const badgeUrl = result.buildFailed
? 'https://img.shields.io/badge/Build%20Scan%E2%84%A2-FAILED-red?logo=Gradle'
: 'https://img.shields.io/badge/Build%20Scan%E2%84%A2-SUCCESS-brightgreen?logo=Gradle';
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">`;
if (result.buildScanUri) {
return `<a href="${result.buildScanUri}" rel="nofollow">${badgeHtml}</a>`;
}
return badgeHtml;
const targetUrl = result.buildScanUri ? result.buildScanUri : '#';
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.

17 changes: 8 additions & 9 deletions dist/post/index.js
Expand Up @@ -64018,6 +64018,7 @@ class GradleStateCache {
fs_1.default.appendFileSync(propertiesFile, 'org.gradle.daemon=false');
const initScriptFilenames = [
'build-result-capture.init.gradle',
'build-result-capture-service.plugin.groovy',
'project-root-capture.init.gradle',
'project-root-capture.plugin.groovy'
];
Expand Down Expand Up @@ -64342,7 +64343,7 @@ class ConfigurationCacheEntryExtractor extends AbstractEntryExtractor {
});
}
getProjectRoots() {
const projectList = path_1.default.resolve(this.gradleUserHome, cache_base_1.PROJECT_ROOTS_FILE);
const projectList = path_1.default.resolve(process.env['RUNNER_TEMP'], cache_base_1.PROJECT_ROOTS_FILE);
if (!fs_1.default.existsSync(projectList)) {
core.info(`Missing project list file ${projectList}`);
return [];
Expand Down Expand Up @@ -64610,7 +64611,7 @@ function isCacheDisabled() {
}
exports.isCacheDisabled = isCacheDisabled;
function isCacheReadOnly() {
return core.getBooleanInput(CACHE_READONLY_PARAMETER);
return !isCacheWriteOnly() && core.getBooleanInput(CACHE_READONLY_PARAMETER);
}
exports.isCacheReadOnly = isCacheReadOnly;
function isCacheWriteOnly() {
Expand Down Expand Up @@ -64951,14 +64952,12 @@ function writeSummaryTable(results) {
core.summary.addRaw('\n');
}
function renderOutcome(result) {
const badgeUrl = result.buildFailed
? 'https://img.shields.io/badge/Build%20Scan%E2%84%A2-FAILED-red?logo=Gradle'
: 'https://img.shields.io/badge/Build%20Scan%E2%84%A2-SUCCESS-brightgreen?logo=Gradle';
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">`;
if (result.buildScanUri) {
return `<a href="${result.buildScanUri}" rel="nofollow">${badgeHtml}</a>`;
}
return badgeHtml;
const targetUrl = result.buildScanUri ? result.buildScanUri : '#';
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.

1 change: 1 addition & 0 deletions src/cache-base.ts
Expand Up @@ -172,6 +172,7 @@ export class GradleStateCache {

const initScriptFilenames = [
'build-result-capture.init.gradle',
'build-result-capture-service.plugin.groovy',
'project-root-capture.init.gradle',
'project-root-capture.plugin.groovy'
]
Expand Down
2 changes: 1 addition & 1 deletion src/cache-extract-entries.ts
Expand Up @@ -387,7 +387,7 @@ export class ConfigurationCacheEntryExtractor extends AbstractEntryExtractor {
* set of project roots, to allow saving of configuration-cache entries for each.
*/
private getProjectRoots(): string[] {
const projectList = path.resolve(this.gradleUserHome, PROJECT_ROOTS_FILE)
const projectList = path.resolve(process.env['RUNNER_TEMP']!, PROJECT_ROOTS_FILE)
if (!fs.existsSync(projectList)) {
core.info(`Missing project list file ${projectList}`)
return []
Expand Down
13 changes: 5 additions & 8 deletions src/job-summary.ts
Expand Up @@ -60,13 +60,10 @@ function writeSummaryTable(results: BuildResult[]): void {
}

function renderOutcome(result: BuildResult): string {
const badgeUrl = result.buildFailed
? 'https://img.shields.io/badge/Build%20Scan%E2%84%A2-FAILED-red?logo=Gradle'
: 'https://img.shields.io/badge/Build%20Scan%E2%84%A2-SUCCESS-brightgreen?logo=Gradle'
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">`

if (result.buildScanUri) {
return `<a href="${result.buildScanUri}" rel="nofollow">${badgeHtml}</a>`
}
return badgeHtml
const targetUrl = result.buildScanUri ? result.buildScanUri : '#'
return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`
}
46 changes: 46 additions & 0 deletions src/resources/build-result-capture-service.plugin.groovy
@@ -0,0 +1,46 @@
import org.gradle.tooling.events.*
import org.gradle.tooling.events.task.*
import org.gradle.util.GradleVersion

// Can't use settingsEvaluated since this script is applied inside a settingsEvaluated handler
// But projectsEvaluated is good enough, since the build service won't catch configuration failures anyway
projectsEvaluated {
def projectTracker = gradle.sharedServices.registerIfAbsent("gradle-build-action-buildResultsRecorder", BuildResultsRecorder, { spec ->
spec.getParameters().getRootProject().set(gradle.rootProject.name)
spec.getParameters().getRequestedTasks().set(gradle.startParameter.taskNames.join(" "))
spec.getParameters().getInvocationId().set(gradle.ext.invocationId)
})

gradle.services.get(BuildEventsListenerRegistry).onTaskCompletion(projectTracker)
}

abstract class BuildResultsRecorder implements BuildService<BuildResultsRecorder.Params>, OperationCompletionListener, AutoCloseable {
private boolean buildFailed = false
interface Params extends BuildServiceParameters {
Property<String> getRootProject()
Property<String> getRequestedTasks()
Property<String> getInvocationId()
}

public void onFinish(FinishEvent finishEvent) {
if (finishEvent instanceof TaskFinishEvent && finishEvent.result instanceof TaskFailureResult) {
buildFailed = true
}
}

@Override
public void close() {
def buildResults = [
rootProject: getParameters().getRootProject().get(),
requestedTasks: getParameters().getRequestedTasks().get(),
gradleVersion: GradleVersion.current().version,
buildFailed: buildFailed,
buildScanUri: null
]

def buildResultsDir = new File(System.getenv("RUNNER_TEMP"), ".build-results")
buildResultsDir.mkdirs()
def buildResultsFile = new File(buildResultsDir, System.getenv("GITHUB_ACTION") + getParameters().getInvocationId().get() + ".json")
buildResultsFile << groovy.json.JsonOutput.toJson(buildResults)
}
}
63 changes: 52 additions & 11 deletions src/resources/build-result-capture.init.gradle
Expand Up @@ -7,25 +7,38 @@ import org.gradle.util.GradleVersion
def isTopLevelBuild = gradle.getParent() == null
if (isTopLevelBuild) {
def version = GradleVersion.current().baseVersion
def atLeastGradle4 = version >= GradleVersion.version("4.0")

def atLeastGradle3 = version >= GradleVersion.version("3.0")
def atLeastGradle6 = version >= GradleVersion.version("6.0")

def invocationId = "-${System.currentTimeMillis()}"

if (atLeastGradle6) {
def useBuildService = version >= GradleVersion.version("6.6")
settingsEvaluated { settings ->
// The `buildScanPublished` hook is the only way to capture the build scan URI.
if (settings.pluginManager.hasPlugin("com.gradle.enterprise")) {
registerCallbacks(settings.extensions["gradleEnterprise"].buildScan, settings.rootProject.name)
captureUsingBuildScanPublished(settings.extensions["gradleEnterprise"].buildScan, settings.rootProject.name, invocationId)
}
// We also need to add hooks in case the plugin is applied but no build scan is published
if (useBuildService) {
captureUsingBuildService(settings, invocationId)
} else {
captureUsingBuildFinished(gradle, invocationId)
}
}
} else if (atLeastGradle4) {
} else if (atLeastGradle3) {
projectsEvaluated { gradle ->
if (gradle.rootProject.pluginManager.hasPlugin("com.gradle.build-scan")) {
registerCallbacks(gradle.rootProject.extensions["buildScan"], gradle.rootProject.name)
captureUsingBuildScanPublished(gradle.rootProject.extensions["buildScan"], gradle.rootProject.name, invocationId)
}
// We need to capture in buildFinished in case the plugin is applied but no build scan is published
captureUsingBuildFinished(gradle, invocationId)
}
}
}

def registerCallbacks(buildScanExtension, rootProjectName) {
def captureUsingBuildScanPublished(buildScanExtension, rootProjectName, invocationId) {
buildScanExtension.with {
def requestedTasks = gradle.startParameter.taskNames.join(" ")
def gradleVersion = GradleVersion.current().version
Expand All @@ -36,10 +49,6 @@ def registerCallbacks(buildScanExtension, rootProjectName) {
}

buildScanPublished { buildScan ->
def buildResultsDir = new File(System.getenv("RUNNER_TEMP"), ".build-results")
buildResultsDir.mkdirs()

def buildResultsFile = new File(buildResultsDir, System.getenv("GITHUB_ACTION") + System.currentTimeMillis() + ".json")

def buildScanUri = buildScan.buildScanUri.toASCIIString()
def buildResults = [
Expand All @@ -49,9 +58,41 @@ def registerCallbacks(buildScanExtension, rootProjectName) {
buildFailed: buildFailed,
buildScanUri: buildScanUri
]
buildResultsFile << groovy.json.JsonOutput.toJson(buildResults)

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)
}

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

def captureUsingBuildFinished(gradle, invocationId) {
gradle.buildFinished { result ->
def buildResults = [
rootProject: gradle.rootProject.name,
requestedTasks: gradle.startParameter.taskNames.join(" "),
gradleVersion: GradleVersion.current().version,
buildFailed: result.failure != null,
buildScanUri: null
]

def buildResultsDir = new File(System.getenv("RUNNER_TEMP"), ".build-results")
buildResultsDir.mkdirs()
def buildResultsFile = new File(buildResultsDir, System.getenv("GITHUB_ACTION") + invocationId + ".json")
buildResultsFile << groovy.json.JsonOutput.toJson(buildResults)
}
}

def captureUsingBuildService(settings, invocationId) {
gradle.ext.invocationId = invocationId
apply from: 'build-result-capture-service.plugin.groovy'
}
2 changes: 1 addition & 1 deletion src/resources/project-root-capture.plugin.groovy
Expand Up @@ -10,7 +10,7 @@ import org.gradle.tooling.events.*

settingsEvaluated { settings ->
def rootDir = settings.rootDir.absolutePath
def rootListLocation = new File(settings.gradle.gradleUserHomeDir, "project-roots.txt").absolutePath
def rootListLocation = new File(System.getenv("RUNNER_TEMP"), "project-roots.txt").absolutePath

def projectTracker = gradle.sharedServices.registerIfAbsent("gradle-build-action-projectRootTracker", ProjectTracker, { spec ->
spec.getParameters().getRootDir().set(rootDir);
Expand Down
2 changes: 2 additions & 0 deletions test/test-init-scripts/.gitignore
@@ -0,0 +1,2 @@
build
.gradle