mirror of
https://github.com/gradle/actions.git
synced 2025-08-20 16:41:28 +08:00
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
112 lines
3.9 KiB
TypeScript
112 lines
3.9 KiB
TypeScript
import * as core from '@actions/core'
|
|
import * as exec from '@actions/exec'
|
|
|
|
import which from 'which'
|
|
import * as semver from 'semver'
|
|
import * as provisioner from './provision'
|
|
import * as gradlew from './gradlew'
|
|
|
|
export async function provisionAndMaybeExecute(
|
|
gradleVersion: string,
|
|
buildRootDirectory: string,
|
|
args: string[]
|
|
): Promise<void> {
|
|
// Download and install Gradle if required
|
|
const executable = await provisioner.provisionGradle(gradleVersion)
|
|
|
|
// Only execute if arguments have been provided
|
|
if (args.length > 0) {
|
|
await executeGradleBuild(executable, buildRootDirectory, args)
|
|
}
|
|
}
|
|
|
|
async function executeGradleBuild(executable: string | undefined, root: string, args: string[]): Promise<void> {
|
|
// Use the provided executable, or look for a Gradle wrapper script to run
|
|
const toExecute = executable ?? gradlew.gradleWrapperScript(root)
|
|
|
|
const status: number = await exec.exec(toExecute, args, {
|
|
cwd: root,
|
|
ignoreReturnCode: true
|
|
})
|
|
|
|
if (status !== 0) {
|
|
core.setFailed(`Gradle build failed: see console output for details`)
|
|
}
|
|
}
|
|
|
|
export function versionIsAtLeast(actualVersion: string, requiredVersion: string): boolean {
|
|
if (actualVersion === requiredVersion) {
|
|
return true
|
|
}
|
|
|
|
const actual = new GradleVersion(actualVersion)
|
|
const required = new GradleVersion(requiredVersion)
|
|
|
|
const actualSemver = semver.coerce(actual.versionPart)!
|
|
const comparisonSemver = semver.coerce(required.versionPart)!
|
|
|
|
if (semver.gt(actualSemver, comparisonSemver)) {
|
|
return true // Actual version is greater than comparison. So it's at least as new.
|
|
}
|
|
if (semver.lt(actualSemver, comparisonSemver)) {
|
|
return false // Actual version is less than comparison. So it's not as new.
|
|
}
|
|
|
|
// Actual and required version numbers are equal, so compare the other parts
|
|
|
|
if (actual.snapshotPart || required.snapshotPart) {
|
|
if (actual.snapshotPart && !required.snapshotPart && !required.stagePart) {
|
|
return false // Actual has a snapshot, but required is a plain version. Required is newer.
|
|
}
|
|
if (required.snapshotPart && !actual.snapshotPart && !actual.stagePart) {
|
|
return true // Required has a snapshot, but actual is a plain version. Actual is newer.
|
|
}
|
|
|
|
return false // Cannot compare case where both versions have a snapshot or stage
|
|
}
|
|
|
|
if (actual.stagePart) {
|
|
if (required.stagePart) {
|
|
return actual.stagePart >= required.stagePart // Compare stages for newer
|
|
}
|
|
|
|
return false // Actual has a stage, but required does not. So required is always newer.
|
|
}
|
|
|
|
return true // Actual has no stage part or snapshot part, so it cannot be older than required.
|
|
}
|
|
|
|
export async function findGradleExecutableOnPath(): Promise<string | null> {
|
|
return await which('gradle', {nothrow: true})
|
|
}
|
|
|
|
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 {
|
|
const regex = /Gradle (\d+\.\d+(\.\d+)?(-.*)?)/
|
|
const versionString = output.match(regex)?.[1]
|
|
return versionString
|
|
}
|
|
|
|
class GradleVersion {
|
|
static PATTERN = /((\d+)(\.\d+)+)(-([a-z]+)-(\w+))?(-(SNAPSHOT|\d{14}([-+]\d{4})?))?/
|
|
|
|
versionPart: string
|
|
stagePart: string
|
|
snapshotPart: string
|
|
|
|
constructor(readonly version: string) {
|
|
const matcher = GradleVersion.PATTERN.exec(version)
|
|
if (!matcher) {
|
|
throw new Error(`'${version}' is not a valid Gradle version string (examples: '1.0', '1.0-rc-1')`)
|
|
}
|
|
|
|
this.versionPart = matcher[1]
|
|
this.stagePart = matcher[4]
|
|
this.snapshotPart = matcher[7]
|
|
}
|
|
}
|