From 6d20c164627f89d66d96f4330e072f78b60c2750 Mon Sep 17 00:00:00 2001 From: daz Date: Thu, 18 Jul 2024 13:38:19 -0600 Subject: [PATCH 1/5] Use `settingsEvaluated` in preference to `projectsEvaluated` in init-script --- ....build-result-capture-service.plugin.groovy | 8 +++----- ...le-actions.build-result-capture.init.gradle | 18 +++++++++--------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy b/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy index e1adda6..6ea2c5a 100644 --- a/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy +++ b/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy @@ -2,12 +2,10 @@ 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 { +settingsEvaluated { settings -> def projectTracker = gradle.sharedServices.registerIfAbsent("gradle-action-buildResultsRecorder", BuildResultsRecorder, { spec -> - spec.getParameters().getRootProjectName().set(gradle.rootProject.name) - spec.getParameters().getRootProjectDir().set(gradle.rootProject.rootDir.absolutePath) + spec.getParameters().getRootProjectName().set(settings.rootProject.name) + spec.getParameters().getRootProjectDir().set(settings.rootDir.absolutePath) spec.getParameters().getRequestedTasks().set(gradle.startParameter.taskNames.join(" ")) spec.getParameters().getGradleHomeDir().set(gradle.gradleHomeDir.absolutePath) spec.getParameters().getInvocationId().set(gradle.ext.invocationId) diff --git a/sources/src/resources/init-scripts/gradle-actions.build-result-capture.init.gradle b/sources/src/resources/init-scripts/gradle-actions.build-result-capture.init.gradle index 160afe4..5cfca2a 100644 --- a/sources/src/resources/init-scripts/gradle-actions.build-result-capture.init.gradle +++ b/sources/src/resources/init-scripts/gradle-actions.build-result-capture.init.gradle @@ -21,16 +21,16 @@ if (isTopLevelBuild) { def invocationId = "-${System.currentTimeMillis()}" if (atLeastGradle6) { + // By default, use standard mechanisms to capture build results def useBuildService = version >= GradleVersion.version("6.6") + if (useBuildService) { + captureUsingBuildService(invocationId) + } else { + captureUsingBuildFinished(gradle, invocationId) + } + + // Use the Develocity plugin to also capture build scan links, when available settingsEvaluated { settings -> - // By default, use standard mechanisms to capture build results - if (useBuildService) { - captureUsingBuildService(settings, invocationId) - } else { - captureUsingBuildFinished(gradle, invocationId) - } - - settings.pluginManager.withPlugin(GE_PLUGIN_ID) { // Only execute if develocity plugin isn't applied. if (!settings.extensions.findByName(DEVELOCITY_EXTENSION)) { @@ -100,7 +100,7 @@ def captureUsingBuildFinished(gradle, invocationId) { } } -def captureUsingBuildService(settings, invocationId) { +def captureUsingBuildService(invocationId) { gradle.ext.invocationId = invocationId apply from: 'gradle-actions.build-result-capture-service.plugin.groovy' } From 94355bbb2f6dfa82391dddba71f3360d4ef3baab Mon Sep 17 00:00:00 2001 From: daz Date: Thu, 18 Jul 2024 14:33:02 -0600 Subject: [PATCH 2/5] Split build-results and build-scan capture into separate files --- ...build-result-capture-service.plugin.groovy | 6 +- ...e-actions.build-result-capture.init.gradle | 121 +++++++----------- .../TestBuildResultRecorder.groovy | 21 ++- 3 files changed, 63 insertions(+), 85 deletions(-) diff --git a/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy b/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy index 6ea2c5a..7f6d9da 100644 --- a/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy +++ b/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy @@ -38,9 +38,7 @@ abstract class BuildResultsRecorder implements BuildService= GradleVersion.version("3.0") @@ -26,7 +27,7 @@ if (isTopLevelBuild) { if (useBuildService) { captureUsingBuildService(invocationId) } else { - captureUsingBuildFinished(gradle, invocationId) + captureUsingBuildFinished(gradle, invocationId, resultsWriter) } // Use the Develocity plugin to also capture build scan links, when available @@ -34,46 +35,63 @@ if (isTopLevelBuild) { settings.pluginManager.withPlugin(GE_PLUGIN_ID) { // Only execute if develocity plugin isn't applied. if (!settings.extensions.findByName(DEVELOCITY_EXTENSION)) { - captureUsingBuildScanPublished(settings.extensions[GE_EXTENSION].buildScan, settings.rootProject, invocationId) + captureUsingBuildScanPublished(settings.extensions[GE_EXTENSION].buildScan, invocationId, resultsWriter) } } settings.pluginManager.withPlugin(DEVELOCITY_PLUGIN_ID) { - captureUsingBuildScanPublished(settings.extensions[DEVELOCITY_EXTENSION].buildScan, settings.rootProject, invocationId) + captureUsingBuildScanPublished(settings.extensions[DEVELOCITY_EXTENSION].buildScan, invocationId, resultsWriter) } } } else if (atLeastGradle3) { projectsEvaluated { gradle -> // By default, use 'buildFinished' to capture build results - captureUsingBuildFinished(gradle, invocationId) + captureUsingBuildFinished(gradle, invocationId, resultsWriter) gradle.rootProject.pluginManager.withPlugin(BUILD_SCAN_PLUGIN_ID) { // Only execute if develocity plugin isn't applied. if (!gradle.rootProject.extensions.findByName(DEVELOCITY_EXTENSION)) { - captureUsingBuildScanPublished(gradle.rootProject.extensions[BUILD_SCAN_EXTENSION], gradle.rootProject, invocationId) + captureUsingBuildScanPublished(gradle.rootProject.extensions[BUILD_SCAN_EXTENSION], invocationId, resultsWriter) } } gradle.rootProject.pluginManager.withPlugin(DEVELOCITY_PLUGIN_ID) { - captureUsingBuildScanPublished(gradle.rootProject.extensions[DEVELOCITY_EXTENSION].buildScan, gradle.rootProject, invocationId) + captureUsingBuildScanPublished(gradle.rootProject.extensions[DEVELOCITY_EXTENSION].buildScan, invocationId, resultsWriter) } } } } +def captureUsingBuildService(invocationId) { + gradle.ext.invocationId = invocationId + apply from: 'gradle-actions.build-result-capture-service.plugin.groovy' +} + +void captureUsingBuildFinished(gradle, String invocationId, ResultsWriter resultsWriter) { + gradle.buildFinished { result -> + println "Got buildFinished: ${result}" + def 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 + ] + resultsWriter.writeToResultsFile("build-results", invocationId, buildResults) + } +} + // The `buildScanPublished` hook allows the capture of the Build Scan URI. // Results captured this way will overwrite any results from 'buildFinished'. -def captureUsingBuildScanPublished(buildScanExtension, rootProject, invocationId) { +void captureUsingBuildScanPublished(buildScanExtension, String invocationId, ResultsWriter resultsWriter) { buildScanExtension.with { - def buildResults = new BuildResults(invocationId, gradle, rootProject) - - buildFinished { result -> - buildResults.setBuildResult(result) - } - buildScanPublished { buildScan -> - buildResults.setBuildScanUri(buildScan.buildScanUri.toASCIIString()) - buildResults.writeToResultsFile(true) + def scanResults = [ + buildScanUri: buildScan.buildScanUri.toASCIIString(), + buildScanFailed: false + ] + resultsWriter.writeToResultsFile("build-scans", invocationId, scanResults) def githubOutput = System.getenv("GITHUB_OUTPUT") if (githubOutput) { @@ -85,63 +103,17 @@ def captureUsingBuildScanPublished(buildScanExtension, rootProject, invocationId } onError { error -> - buildResults.setBuildScanFailed() - buildResults.writeToResultsFile(true) + def scanResults = [ + buildScanUri: null, + buildScanFailed: true + ] + resultsWriter.writeToResultsFile("build-scans", invocationId, scanResults) } } } -def captureUsingBuildFinished(gradle, invocationId) { - gradle.buildFinished { result -> - println "Got buildFinished: ${result}" - def buildResults = new BuildResults(invocationId, gradle, gradle.rootProject) - buildResults.setBuildResult(result) - buildResults.writeToResultsFile(false) - } -} - -def captureUsingBuildService(invocationId) { - gradle.ext.invocationId = invocationId - apply from: 'gradle-actions.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: false, - buildScanUri: null, - buildScanFailed: false - ] - } - - def setBuildResult(def result) { - try { - // Gradle and old Build Scan/Gradle Enterprise plugins report a single optional failure in the build result - buildResults['buildFailed'] = result.failure != null - } catch (Exception e) { - // Develocity plugin unwraps all build failures and reports them as a mandatory array - buildResults['buildFailed'] = !result.failures.empty - } - } - - def setBuildScanUri(def buildScanUrl) { - buildResults['buildScanUri'] = buildScanUrl - } - - def setBuildScanFailed() { - buildResults['buildScanFailed'] = true - } - - def writeToResultsFile(boolean overwrite) { +class ResultsWriter { + void writeToResultsFile(String subDir, String invocationId, def content) { def runnerTempDir = System.getProperty("RUNNER_TEMP") ?: System.getenv("RUNNER_TEMP") def githubActionStep = System.getProperty("GITHUB_ACTION") ?: System.getenv("GITHUB_ACTION") if (!runnerTempDir || !githubActionStep) { @@ -149,19 +121,12 @@ class BuildResults { } try { - def buildResultsDir = new File(runnerTempDir, ".build-results") + def buildResultsDir = new File(runnerTempDir, ".gradle-actions/${subDir}") buildResultsDir.mkdirs() def buildResultsFile = new File(buildResultsDir, githubActionStep + invocationId + ".json") - - // 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) + if (!buildResultsFile.exists()) { + buildResultsFile << groovy.json.JsonOutput.toJson(content) } - } catch (Exception e) { println "\ngradle action failed to write build-results file. Will continue.\n> ${e.getLocalizedMessage()}" } diff --git a/sources/test/init-scripts/src/test/groovy/com/gradle/gradlebuildaction/TestBuildResultRecorder.groovy b/sources/test/init-scripts/src/test/groovy/com/gradle/gradlebuildaction/TestBuildResultRecorder.groovy index a73ad4d..a8f163f 100644 --- a/sources/test/init-scripts/src/test/groovy/com/gradle/gradlebuildaction/TestBuildResultRecorder.groovy +++ b/sources/test/init-scripts/src/test/groovy/com/gradle/gradlebuildaction/TestBuildResultRecorder.groovy @@ -122,6 +122,7 @@ class TestBuildResultRecorder extends BaseInitScriptTest { then: assertResults('help', testGradleVersion, false, true) assert buildResultFile.delete() + assert scanResultFile.delete() when: run(['help', '--configuration-cache'], testGradleVersion.gradleVersion) @@ -240,12 +241,16 @@ class TestBuildResultRecorder extends BaseInitScriptTest { assert results['gradleVersion'] == testGradleVersion.gradleVersion.version assert results['gradleHomeDir'] != null assert results['buildFailed'] == hasFailure - assert results['buildScanUri'] == (hasBuildScan ? "${mockScansServer.address}s/${PUBLIC_BUILD_SCAN_ID}" : null) - assert results['buildScanFailed'] == scanUploadFailed + + if (hasBuildScan || scanUploadFailed) { + def scanResults = new JsonSlurper().parse(scanResultFile) + assert scanResults['buildScanUri'] == (hasBuildScan ? "${mockScansServer.address}s/${PUBLIC_BUILD_SCAN_ID}" : null) + assert scanResults['buildScanFailed'] == scanUploadFailed + } } private File getBuildResultFile() { - def buildResultsDir = new File(testProjectDir, '.build-results') + def buildResultsDir = new File(testProjectDir, '.gradle-actions/build-results') assert buildResultsDir.directory assert buildResultsDir.listFiles().size() == 1 def resultsFile = buildResultsDir.listFiles()[0] @@ -253,4 +258,14 @@ class TestBuildResultRecorder extends BaseInitScriptTest { assert resultsFile.text.count('rootProjectName') == 1 return resultsFile } + + private File getScanResultFile() { + def buildResultsDir = new File(testProjectDir, '.gradle-actions/build-scans') + assert buildResultsDir.directory + assert buildResultsDir.listFiles().size() == 1 + def resultsFile = buildResultsDir.listFiles()[0] + assert resultsFile.name.startsWith('github-step-id') + assert resultsFile.text.count('buildScanUri') == 1 + return resultsFile + } } From 579a013225cbca6ecef64dece3778196fd85d7b0 Mon Sep 17 00:00:00 2001 From: daz Date: Thu, 18 Jul 2024 14:52:46 -0600 Subject: [PATCH 3/5] Capture config-cache hit in build results --- ...build-result-capture-service.plugin.groovy | 29 ++++++++++--- ...e-actions.build-result-capture.init.gradle | 3 +- .../TestBuildResultRecorder.groovy | 41 +++++++++++-------- 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy b/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy index 7f6d9da..c2d938b 100644 --- a/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy +++ b/sources/src/resources/init-scripts/gradle-actions.build-result-capture-service.plugin.groovy @@ -1,5 +1,10 @@ import org.gradle.tooling.events.* import org.gradle.tooling.events.task.* +import org.gradle.internal.operations.* +import org.gradle.initialization.* +import org.gradle.api.internal.tasks.execution.* +import org.gradle.execution.* +import org.gradle.internal.build.event.BuildEventListenerRegistryInternal import org.gradle.util.GradleVersion settingsEvaluated { settings -> @@ -11,11 +16,12 @@ settingsEvaluated { settings -> spec.getParameters().getInvocationId().set(gradle.ext.invocationId) }) - gradle.services.get(BuildEventsListenerRegistry).onTaskCompletion(projectTracker) + gradle.services.get(BuildEventListenerRegistryInternal).onOperationCompletion(projectTracker) } -abstract class BuildResultsRecorder implements BuildService, OperationCompletionListener, AutoCloseable { +abstract class BuildResultsRecorder implements BuildService, BuildOperationListener, AutoCloseable { private boolean buildFailed = false + private boolean configCacheHit = true interface Params extends BuildServiceParameters { Property getRootProjectName() Property getRootProjectDir() @@ -24,9 +30,19 @@ abstract class BuildResultsRecorder implements BuildService getInvocationId() } - public void onFinish(FinishEvent finishEvent) { - if (finishEvent instanceof TaskFinishEvent && finishEvent.result instanceof TaskFailureResult) { - buildFailed = true + void started(BuildOperationDescriptor buildOperation, OperationStartEvent startEvent) {} + + void progress(OperationIdentifier operationIdentifier, OperationProgressEvent progressEvent) {} + + void finished(BuildOperationDescriptor buildOperation, OperationFinishEvent finishEvent) { + if (buildOperation.details in EvaluateSettingsBuildOperationType.Details) { + // Got EVALUATE SETTINGS event: not a config-cache hit" + configCacheHit = false + } + if (buildOperation.details in RunRootBuildWorkBuildOperationType.Details) { + if (finishEvent.failure != null) { + buildFailed = true + } } } @@ -38,7 +54,8 @@ abstract class BuildResultsRecorder implements BuildService Date: Thu, 18 Jul 2024 15:22:38 -0600 Subject: [PATCH 4/5] Adapt for new structure of build results --- sources/src/build-results.ts | 23 +++++++++++++++++++++-- sources/test/jest/job-summary.test.ts | 1 + 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/sources/src/build-results.ts b/sources/src/build-results.ts index 8be44f3..9e3b1b4 100644 --- a/sources/src/build-results.ts +++ b/sources/src/build-results.ts @@ -8,6 +8,7 @@ export interface BuildResult { get gradleVersion(): string get gradleHomeDir(): string get buildFailed(): boolean + get configCacheHit(): boolean get buildScanUri(): string get buildScanFailed(): boolean } @@ -32,7 +33,9 @@ export class BuildResults { export function loadBuildResults(): BuildResults { const results = getUnprocessedResults().map(filePath => { const content = fs.readFileSync(filePath, 'utf8') - return JSON.parse(content) as BuildResult + const buildResult = JSON.parse(content) as BuildResult + addScanResults(filePath, buildResult) + return buildResult }) return new BuildResults(results) } @@ -42,7 +45,7 @@ export function markBuildResultsProcessed(): void { } function getUnprocessedResults(): string[] { - const buildResultsDir = path.resolve(process.env['RUNNER_TEMP']!, '.build-results') + const buildResultsDir = path.resolve(process.env['RUNNER_TEMP']!, '.gradle-actions', 'build-results') if (!fs.existsSync(buildResultsDir)) { return [] } @@ -57,6 +60,22 @@ function getUnprocessedResults(): string[] { }) } +function addScanResults(buildResultsFile: string, buildResult: BuildResult): void { + const buildScansDir = path.resolve(process.env['RUNNER_TEMP']!, '.gradle-actions', 'build-scans') + if (!fs.existsSync(buildScansDir)) { + return + } + + const buildScanResults = path.resolve(buildScansDir, path.basename(buildResultsFile)) + if (fs.existsSync(buildScanResults)) { + const content = fs.readFileSync(buildScanResults, 'utf8') + const scanResults = JSON.parse(content) + Object.assign(buildResult, scanResults) + } + + return +} + function isProcessed(resultFile: string): boolean { const markerFile = `${resultFile}.processed` return fs.existsSync(markerFile) diff --git a/sources/test/jest/job-summary.test.ts b/sources/test/jest/job-summary.test.ts index 91cffa2..0f5c102 100644 --- a/sources/test/jest/job-summary.test.ts +++ b/sources/test/jest/job-summary.test.ts @@ -10,6 +10,7 @@ const successfulHelpBuild: BuildResult = { gradleVersion: '8.0', gradleHomeDir: '/opt/gradle', buildFailed: false, + configCacheHit: false, buildScanUri: 'https://scans.gradle.com/s/abc123', buildScanFailed: false } From a77cb2b0f88183c4b03dad0e1e70efeeb575319c Mon Sep 17 00:00:00 2001 From: daz Date: Thu, 18 Jul 2024 21:40:13 -0600 Subject: [PATCH 5/5] Add test for no cache-cleanup with config-cache hit --- ...integ-test-restore-configuration-cache.yml | 82 ++++++++++++++----- sources/src/build-results.ts | 4 + sources/src/caching/cache-reporting.ts | 3 + sources/src/caching/caches.ts | 12 ++- 4 files changed, 80 insertions(+), 21 deletions(-) diff --git a/.github/workflows/integ-test-restore-configuration-cache.yml b/.github/workflows/integ-test-restore-configuration-cache.yml index ab578e9..d681a27 100644 --- a/.github/workflows/integ-test-restore-configuration-cache.yml +++ b/.github/workflows/integ-test-restore-configuration-cache.yml @@ -43,6 +43,7 @@ jobs: uses: ./setup-gradle with: cache-read-only: false # For testing, allow writing cache entries on non-default branches + cache-write-only: true # Ensure we start with a clean cache entry cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} gradle-version: 8.6 - name: Groovy build with configuration-cache enabled @@ -52,6 +53,7 @@ jobs: verify-build-groovy: env: GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-groovy + GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_1 needs: seed-build-groovy strategy: fail-fast: false @@ -64,6 +66,47 @@ jobs: - name: Initialize integ-test uses: ./.github/actions/init-integ-test + - name: Setup Java to ensure consistency + uses: actions/setup-java@v4 + with: + distribution: 'liberica' + java-version: 17 + - name: Setup Gradle + uses: ./setup-gradle + with: + cache-read-only: false + cache-cleanup: on-success + cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + gradle-version: 8.6 + - name: Groovy build with configuration-cache enabled + id: execute + working-directory: .github/workflow-samples/groovy-dsl + run: gradle test --configuration-cache + - name: Verify configuration-cache hit + shell: bash + run: | + if [ -e ".github/workflow-samples/groovy-dsl/task-configured.txt" ]; then + echo "Configuration cache was not used - task was configured unexpectedly" + exit 1 + fi + + # Ensure that cache-cleanup doesn't remove all necessary files + verify-no-cache-cleanup-groovy: + env: + GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-groovy + GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_2 + needs: verify-build-groovy + strategy: + fail-fast: false + matrix: + os: ${{fromJSON(inputs.runner-os)}} + runs-on: ${{ matrix.os }} + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Initialize integ-test + uses: ./.github/actions/init-integ-test + - name: Setup Java to ensure consistency uses: actions/setup-java@v4 with: @@ -79,19 +122,19 @@ jobs: id: execute working-directory: .github/workflow-samples/groovy-dsl run: gradle test --configuration-cache - - name: Check that configuration-cache was used - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs') - if (fs.existsSync('.github/workflow-samples/groovy-dsl/task-configured.txt')) { - core.setFailed('Configuration cache was not used - task was configured unexpectedly') - } + - name: Verify configuration-cache hit + shell: bash + run: | + if [ -e ".github/workflow-samples/groovy-dsl/task-configured.txt" ]; then + echo "Configuration cache was not used - task was configured unexpectedly" + exit 1 + fi # Check that the build can run when no extracted cache entries are restored gradle-user-home-not-fully-restored: env: GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-groovy + GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_x needs: seed-build-groovy strategy: fail-fast: false @@ -144,6 +187,7 @@ jobs: uses: ./setup-gradle with: cache-read-only: false # For testing, allow writing cache entries on non-default branches + cache-write-only: true # Ensure we start with a clean cache entry cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} gradle-version: 8.6 - name: Execute 'help' with configuration-cache enabled @@ -152,7 +196,8 @@ jobs: modify-build-kotlin: env: - GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-kotlin-modified + GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-kotlin + GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_1 needs: seed-build-kotlin strategy: fail-fast: false @@ -183,7 +228,8 @@ jobs: # Test restore configuration-cache from the third build invocation verify-build-kotlin: env: - GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-kotlin-modified + GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-kotlin + GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_2 needs: modify-build-kotlin strategy: fail-fast: false @@ -211,12 +257,10 @@ jobs: id: execute working-directory: .github/workflow-samples/kotlin-dsl run: gradle test --configuration-cache - - name: Check that configuration-cache was used - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs') - if (fs.existsSync('.github/workflow-samples/kotlin-dsl/task-configured.txt')) { - core.setFailed('Configuration cache was not used - task was configured unexpectedly') - } - + - name: Verify configuration-cache hit + shell: bash + run: | + if [ -e ".github/workflow-samples/kotlin-dsl/task-configured.txt" ]; then + echo "Configuration cache was not used - task was configured unexpectedly" + exit 1 + fi diff --git a/sources/src/build-results.ts b/sources/src/build-results.ts index 9e3b1b4..82934cf 100644 --- a/sources/src/build-results.ts +++ b/sources/src/build-results.ts @@ -24,6 +24,10 @@ export class BuildResults { return this.results.some(result => result.buildFailed) } + anyConfigCacheHit(): boolean { + return this.results.some(result => result.configCacheHit) + } + uniqueGradleHomes(): string[] { const allHomes = this.results.map(buildResult => buildResult.gradleHomeDir) return Array.from(new Set(allHomes)) diff --git a/sources/src/caching/cache-reporting.ts b/sources/src/caching/cache-reporting.ts index 6dd0183..2ad07eb 100644 --- a/sources/src/caching/cache-reporting.ts +++ b/sources/src/caching/cache-reporting.ts @@ -19,6 +19,9 @@ export const DEFAULT_CLEANUP_ENABLED_REASON = `[Cache cleanup](https://github.co export const CLEANUP_DISABLED_DUE_TO_FAILURE = '[Cache cleanup was disabled due to build failure](https://github.com/gradle/actions/blob/v3/docs/setup-gradle.md#enabling-cache-cleanup). Use `cache-cleanup: always` to override this behavior.' +export const CLEANUP_DISABLED_DUE_TO_CONFIG_CACHE_HIT = + '[Cache cleanup was disabled due to configuration-cache reuse](https://github.com/gradle/actions/blob/v3/docs/setup-gradle.md#enabling-cache-cleanup). This is expected.' + /** * Collects information on what entries were saved and restored during the action. * This information is used to generate a summary of the cache usage. diff --git a/sources/src/caching/caches.ts b/sources/src/caching/caches.ts index 5602a94..9ea0e5f 100644 --- a/sources/src/caching/caches.ts +++ b/sources/src/caching/caches.ts @@ -1,5 +1,10 @@ import * as core from '@actions/core' -import {CacheListener, EXISTING_GRADLE_HOME, CLEANUP_DISABLED_DUE_TO_FAILURE} from './cache-reporting' +import { + CacheListener, + EXISTING_GRADLE_HOME, + CLEANUP_DISABLED_DUE_TO_FAILURE, + CLEANUP_DISABLED_DUE_TO_CONFIG_CACHE_HIT +} from './cache-reporting' import {GradleUserHomeCache} from './gradle-user-home-cache' import {CacheCleaner} from './cache-cleaner' import {DaemonController} from '../daemon-controller' @@ -90,7 +95,10 @@ export async function save( await daemonController.stopAllDaemons() if (cacheConfig.isCacheCleanupEnabled()) { - if (cacheConfig.shouldPerformCacheCleanup(buildResults.anyFailed())) { + if (buildResults.anyConfigCacheHit()) { + core.info('Not performing cache-cleanup due to config-cache reuse') + cacheListener.setCacheCleanupDisabled(CLEANUP_DISABLED_DUE_TO_CONFIG_CACHE_HIT) + } else if (cacheConfig.shouldPerformCacheCleanup(buildResults.anyFailed())) { cacheListener.setCacheCleanupEnabled() await performCacheCleanup(gradleUserHome) } else {