mirror of
https://github.com/gradle/actions.git
synced 2025-08-25 11:22:01 +08:00
Attempt to use gradle wrapper for cache cleanup (#525)
The cache-cleanup operation works by executing Gradle on a dummy project and a custom init-script. The version of Gradle used should be at least as high as the newest version used to run a build. Previously, if the Gradle version on PATH didn't meet this requirement, the action would download and install the required Gradle version. With this PR, the action will now use an existing Gradle wrapper distribution if it meets the requirement. This avoids unnecessary downloads of Gradle versions that are already present on the runner. The logic is: - Determine the newest version of Gradle that was executed during the Job. This is the 'minimum version' for cache cleanup. - Inspect the Gradle version on PATH and any detected wrapper scripts to see if they meet the 'minimum version'. - The first executable that is found to meet the requirements will be used for cache-cleanup. - If no executable is found that meets the requirements, attempt to provision Gradle with the 'minimum version'. Fixes #515
This commit is contained in:
parent
7569aee516
commit
91619fae90
@ -4,8 +4,9 @@ import * as exec from '@actions/exec'
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import * as provisioner from '../execution/provision'
|
import * as provisioner from '../execution/provision'
|
||||||
import {BuildResults} from '../build-results'
|
import {BuildResult, BuildResults} from '../build-results'
|
||||||
import {versionIsAtLeast} from '../execution/gradle'
|
import {versionIsAtLeast} from '../execution/gradle'
|
||||||
|
import {gradleWrapperScript} from '../execution/gradlew'
|
||||||
|
|
||||||
export class CacheCleaner {
|
export class CacheCleaner {
|
||||||
private readonly gradleUserHome: string
|
private readonly gradleUserHome: string
|
||||||
@ -38,7 +39,11 @@ export class CacheCleaner {
|
|||||||
const preferredVersion = buildResults.highestGradleVersion()
|
const preferredVersion = buildResults.highestGradleVersion()
|
||||||
if (preferredVersion && versionIsAtLeast(preferredVersion, '8.11')) {
|
if (preferredVersion && versionIsAtLeast(preferredVersion, '8.11')) {
|
||||||
try {
|
try {
|
||||||
return await provisioner.provisionGradleAtLeast(preferredVersion)
|
const wrapperScripts = buildResults.results
|
||||||
|
.map(result => this.findGradleWrapperScript(result))
|
||||||
|
.filter(Boolean) as string[]
|
||||||
|
|
||||||
|
return await provisioner.provisionGradleWithVersionAtLeast(preferredVersion, wrapperScripts)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignore the case where the preferred version cannot be located in https://services.gradle.org/versions/all.
|
// Ignore the case where the preferred version cannot be located in https://services.gradle.org/versions/all.
|
||||||
// This can happen for snapshot Gradle versions.
|
// This can happen for snapshot Gradle versions.
|
||||||
@ -49,7 +54,17 @@ export class CacheCleaner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to the minimum version required for cache-cleanup
|
// Fallback to the minimum version required for cache-cleanup
|
||||||
return await provisioner.provisionGradleAtLeast('8.11')
|
return await provisioner.provisionGradleWithVersionAtLeast('8.11')
|
||||||
|
}
|
||||||
|
|
||||||
|
private findGradleWrapperScript(result: BuildResult): string | null {
|
||||||
|
try {
|
||||||
|
const wrapperScript = gradleWrapperScript(result.rootProjectDir)
|
||||||
|
return path.resolve(result.rootProjectDir, wrapperScript)
|
||||||
|
} catch (error) {
|
||||||
|
core.debug(`No Gradle Wrapper found for ${result.rootProjectName}: ${error}`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visible for testing
|
// Visible for testing
|
||||||
|
@ -76,15 +76,13 @@ export function versionIsAtLeast(actualVersion: string, requiredVersion: string)
|
|||||||
return true // Actual has no stage part or snapshot part, so it cannot be older than required.
|
return true // Actual has no stage part or snapshot part, so it cannot be older than required.
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findGradleVersionOnPath(): Promise<GradleExecutable | undefined> {
|
export async function findGradleExecutableOnPath(): Promise<string | null> {
|
||||||
const gradleExecutable = await which('gradle', {nothrow: true})
|
return await which('gradle', {nothrow: true})
|
||||||
if (gradleExecutable) {
|
}
|
||||||
const output = await exec.getExecOutput(gradleExecutable, ['-v'], {silent: true})
|
|
||||||
const version = parseGradleVersionFromOutput(output.stdout)
|
|
||||||
return version ? new GradleExecutable(version, gradleExecutable) : undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined
|
export async function determineGradleVersion(gradleExecutable: string): Promise<string | undefined> {
|
||||||
|
const output = await exec.getExecOutput(gradleExecutable, ['-v'], {silent: true})
|
||||||
|
return parseGradleVersionFromOutput(output.stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseGradleVersionFromOutput(output: string): string | undefined {
|
export function parseGradleVersionFromOutput(output: string): string | undefined {
|
||||||
@ -93,13 +91,6 @@ export function parseGradleVersionFromOutput(output: string): string | undefined
|
|||||||
return versionString
|
return versionString
|
||||||
}
|
}
|
||||||
|
|
||||||
class GradleExecutable {
|
|
||||||
constructor(
|
|
||||||
readonly version: string,
|
|
||||||
readonly executable: string
|
|
||||||
) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GradleVersion {
|
class GradleVersion {
|
||||||
static PATTERN = /((\d+)(\.\d+)+)(-([a-z]+)-(\w+))?(-(SNAPSHOT|\d{14}([-+]\d{4})?))?/
|
static PATTERN = /((\d+)(\.\d+)+)(-([a-z]+)-(\w+))?(-(SNAPSHOT|\d{14}([-+]\d{4})?))?/
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import * as core from '@actions/core'
|
|||||||
import * as cache from '@actions/cache'
|
import * as cache from '@actions/cache'
|
||||||
import * as toolCache from '@actions/tool-cache'
|
import * as toolCache from '@actions/tool-cache'
|
||||||
|
|
||||||
import {findGradleVersionOnPath, versionIsAtLeast} from './gradle'
|
import {determineGradleVersion, findGradleExecutableOnPath, versionIsAtLeast} from './gradle'
|
||||||
import * as gradlew from './gradlew'
|
import * as gradlew from './gradlew'
|
||||||
import {handleCacheFailure} from '../caching/cache-utils'
|
import {handleCacheFailure} from '../caching/cache-utils'
|
||||||
import {CacheConfig} from '../configuration'
|
import {CacheConfig} from '../configuration'
|
||||||
@ -25,16 +25,6 @@ export async function provisionGradle(gradleVersion: string): Promise<string | u
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that the Gradle version on PATH is no older than the specified version.
|
|
||||||
* If the version on PATH is older, install the specified version and add it to the PATH.
|
|
||||||
* @return Installed Gradle executable or undefined if no version configured.
|
|
||||||
*/
|
|
||||||
export async function provisionGradleAtLeast(gradleVersion: string): Promise<string> {
|
|
||||||
const installedVersion = await installGradleVersionAtLeast(await gradleRelease(gradleVersion))
|
|
||||||
return addToPath(installedVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addToPath(executable: string): Promise<string> {
|
async function addToPath(executable: string): Promise<string> {
|
||||||
core.addPath(path.dirname(executable))
|
core.addPath(path.dirname(executable))
|
||||||
return executable
|
return executable
|
||||||
@ -106,27 +96,44 @@ async function findGradleVersionDeclaration(version: string): Promise<GradleVers
|
|||||||
|
|
||||||
async function installGradleVersion(versionInfo: GradleVersionInfo): Promise<string> {
|
async function installGradleVersion(versionInfo: GradleVersionInfo): Promise<string> {
|
||||||
return core.group(`Provision Gradle ${versionInfo.version}`, async () => {
|
return core.group(`Provision Gradle ${versionInfo.version}`, async () => {
|
||||||
const gradleOnPath = await findGradleVersionOnPath()
|
const gradleOnPath = await findGradleExecutableOnPath()
|
||||||
if (gradleOnPath?.version === versionInfo.version) {
|
if (gradleOnPath) {
|
||||||
core.info(`Gradle version ${versionInfo.version} is already available on PATH. Not installing.`)
|
const gradleOnPathVersion = await determineGradleVersion(gradleOnPath)
|
||||||
return gradleOnPath.executable
|
if (gradleOnPathVersion === versionInfo.version) {
|
||||||
|
core.info(`Gradle version ${versionInfo.version} is already available on PATH. Not installing.`)
|
||||||
|
return gradleOnPath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return locateGradleAndDownloadIfRequired(versionInfo)
|
return locateGradleAndDownloadIfRequired(versionInfo)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function installGradleVersionAtLeast(versionInfo: GradleVersionInfo): Promise<string> {
|
/**
|
||||||
return core.group(`Provision Gradle >= ${versionInfo.version}`, async () => {
|
* Find (or install) a Gradle executable that meets the specified version requirement.
|
||||||
const gradleOnPath = await findGradleVersionOnPath()
|
* The Gradle version on PATH and all candidates are first checked for version compatibility.
|
||||||
if (gradleOnPath && versionIsAtLeast(gradleOnPath.version, versionInfo.version)) {
|
* If no existing Gradle version meets the requirement, the required version is installed.
|
||||||
core.info(
|
* @return Gradle executable with at least the required version.
|
||||||
`Gradle version ${gradleOnPath.version} is available on PATH and >= ${versionInfo.version}. Not installing.`
|
*/
|
||||||
)
|
export async function provisionGradleWithVersionAtLeast(
|
||||||
return gradleOnPath.executable
|
minimumVersion: string,
|
||||||
|
candidates: string[] = []
|
||||||
|
): Promise<string> {
|
||||||
|
const gradleOnPath = await findGradleExecutableOnPath()
|
||||||
|
const allCandidates = gradleOnPath ? [gradleOnPath, ...candidates] : candidates
|
||||||
|
|
||||||
|
return core.group(`Provision Gradle >= ${minimumVersion}`, async () => {
|
||||||
|
for (const candidate of allCandidates) {
|
||||||
|
const candidateVersion = await determineGradleVersion(candidate)
|
||||||
|
if (candidateVersion && versionIsAtLeast(candidateVersion, minimumVersion)) {
|
||||||
|
core.info(
|
||||||
|
`Gradle version ${candidateVersion} is available at ${candidate} and >= ${minimumVersion}. Not installing.`
|
||||||
|
)
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return locateGradleAndDownloadIfRequired(versionInfo)
|
return locateGradleAndDownloadIfRequired(await gradleRelease(minimumVersion))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user