mirror of
https://github.com/gradle/actions.git
synced 2026-09-10 19:28:33 +08:00
Report EOL and maintenance status for Gradle versions
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
name: Test Gradle support status reporting
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
cache-key-prefix:
|
||||||
|
type: string
|
||||||
|
default: '0'
|
||||||
|
skip-dist:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
SKIP_DIST: ${{ inputs.skip-dist }}
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: gradle-support-status-${{ inputs.cache-key-prefix }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
report-version-status:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
- name: Initialize integ-test
|
||||||
|
uses: ./.github/actions/init-integ-test
|
||||||
|
|
||||||
|
- name: Setup Gradle with an end-of-life version
|
||||||
|
uses: ./setup-gradle
|
||||||
|
with:
|
||||||
|
cache-read-only: false
|
||||||
|
gradle-version: '7.6.4'
|
||||||
|
- name: Build with the end-of-life version
|
||||||
|
id: build-end-of-life
|
||||||
|
working-directory: .github/workflow-samples/no-wrapper
|
||||||
|
run: gradle help "-DgradleVersionCheck=7.6.4"
|
||||||
|
|
||||||
|
- name: Setup Gradle with the current version
|
||||||
|
uses: ./setup-gradle
|
||||||
|
with:
|
||||||
|
gradle-version: current
|
||||||
|
- name: Build with the current version
|
||||||
|
id: build-current
|
||||||
|
working-directory: .github/workflow-samples/no-wrapper
|
||||||
|
run: gradle help
|
||||||
|
|
||||||
|
- name: Check the end-of-life version was reported as 'eol'
|
||||||
|
if: ${{ steps.build-end-of-life.outputs.gradle-version-status != 'eol' }}
|
||||||
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
core.setFailed("Expected Gradle 7.6.4 to be reported as 'eol', but was '${{ steps.build-end-of-life.outputs.gradle-version-status }}'")
|
||||||
|
|
||||||
|
- name: Check the current version was reported as 'active'
|
||||||
|
if: ${{ steps.build-current.outputs.gradle-version-status != 'active' }}
|
||||||
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
core.setFailed("Expected the current Gradle version to be reported as 'active', but was '${{ steps.build-current.outputs.gradle-version-status }}'")
|
||||||
@@ -28,6 +28,11 @@ jobs:
|
|||||||
secrets:
|
secrets:
|
||||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}
|
DEVELOCITY_ACCESS_KEY: ${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}
|
||||||
|
|
||||||
|
gradle-support-status:
|
||||||
|
uses: ./.github/workflows/integ-test-gradle-support-status.yml
|
||||||
|
with:
|
||||||
|
skip-dist: ${{ inputs.skip-dist }}
|
||||||
|
|
||||||
provision-gradle-versions:
|
provision-gradle-versions:
|
||||||
uses: ./.github/workflows/integ-test-provision-gradle-versions.yml
|
uses: ./.github/workflows/integ-test-provision-gradle-versions.yml
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
.git
|
.git
|
||||||
.vscode
|
.vscode
|
||||||
.claude
|
.claude
|
||||||
|
.idea
|
||||||
|
|||||||
@@ -156,3 +156,6 @@ outputs:
|
|||||||
|
|
||||||
gradle-version:
|
gradle-version:
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
gradle-version-status:
|
||||||
|
type: string
|
||||||
|
|||||||
@@ -229,7 +229,11 @@ outputs:
|
|||||||
description: Path to the GitHub Dependency Graph snapshot file generated by a Gradle build. Note that this output applies to a Step executing Gradle, not to the `setup-gradle` Step itself.
|
description: Path to the GitHub Dependency Graph snapshot file generated by a Gradle build. Note that this output applies to a Step executing Gradle, not to the `setup-gradle` Step itself.
|
||||||
gradle-version:
|
gradle-version:
|
||||||
description: Version of Gradle that was setup by the action
|
description: Version of Gradle that was setup by the action
|
||||||
|
gradle-version-status:
|
||||||
|
description: |
|
||||||
|
Support status of the Gradle version that executed the build, following
|
||||||
|
https://docs.gradle.org/current/userguide/feature_lifecycle.html:
|
||||||
|
'active', 'maintenance' or 'eol'
|
||||||
runs:
|
runs:
|
||||||
using: 'node24'
|
using: 'node24'
|
||||||
main: '../dist/dependency-submission/main/index.js'
|
main: '../dist/dependency-submission/main/index.js'
|
||||||
|
|||||||
@@ -179,3 +179,6 @@ outputs:
|
|||||||
|
|
||||||
gradle-version:
|
gradle-version:
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
gradle-version-status:
|
||||||
|
type: string
|
||||||
|
|||||||
@@ -246,7 +246,11 @@ outputs:
|
|||||||
description: Path to the GitHub Dependency Graph snapshot file generated by a Gradle build. Note that this output applies to a Step executing Gradle, not to the `setup-gradle` Step itself.
|
description: Path to the GitHub Dependency Graph snapshot file generated by a Gradle build. Note that this output applies to a Step executing Gradle, not to the `setup-gradle` Step itself.
|
||||||
gradle-version:
|
gradle-version:
|
||||||
description: Version of Gradle that was setup by the action
|
description: Version of Gradle that was setup by the action
|
||||||
|
gradle-version-status:
|
||||||
|
description: |
|
||||||
|
Support status of the Gradle version that executed the build, following
|
||||||
|
https://docs.gradle.org/current/userguide/feature_lifecycle.html:
|
||||||
|
'active', 'maintenance' or 'eol'
|
||||||
runs:
|
runs:
|
||||||
using: 'node24'
|
using: 'node24'
|
||||||
main: '../dist/setup-gradle/main/index.js'
|
main: '../dist/setup-gradle/main/index.js'
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export interface BuildResult {
|
|||||||
get rootProjectDir(): string
|
get rootProjectDir(): string
|
||||||
get requestedTasks(): string
|
get requestedTasks(): string
|
||||||
get gradleVersion(): string
|
get gradleVersion(): string
|
||||||
|
get versionStatus(): string | undefined
|
||||||
get gradleHomeDir(): string
|
get gradleHomeDir(): string
|
||||||
get buildFailed(): boolean
|
get buildFailed(): boolean
|
||||||
get configCacheHit(): boolean
|
get configCacheHit(): boolean
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
export class GradleVersion {
|
||||||
|
static PATTERN = /((\d+)(\.\d+)+)(-([a-z]+)-(\w+))?(-(SNAPSHOT|\d{14}([-+]\d{4})?))?/
|
||||||
|
|
||||||
|
major: number
|
||||||
|
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.major = Number(matcher[2])
|
||||||
|
this.versionPart = matcher[1]
|
||||||
|
this.stagePart = matcher[4]
|
||||||
|
this.snapshotPart = matcher[7]
|
||||||
|
}
|
||||||
|
|
||||||
|
isFinalRelease(): boolean {
|
||||||
|
return !this.stagePart && !this.snapshotPart
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import which from 'which'
|
|||||||
import * as semver from 'semver'
|
import * as semver from 'semver'
|
||||||
import * as provisioner from './provision'
|
import * as provisioner from './provision'
|
||||||
import * as gradlew from './gradlew'
|
import * as gradlew from './gradlew'
|
||||||
|
import {GradleVersion} from './gradle-version'
|
||||||
|
|
||||||
export async function provisionAndMaybeExecute(
|
export async function provisionAndMaybeExecute(
|
||||||
gradleVersion: string,
|
gradleVersion: string,
|
||||||
@@ -90,22 +91,3 @@ export function parseGradleVersionFromOutput(output: string): string | undefined
|
|||||||
const versionString = output.match(regex)?.[1]
|
const versionString = output.match(regex)?.[1]
|
||||||
return versionString
|
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]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
|
import {BuildResult} from './build-results'
|
||||||
|
import {GradleVersion} from './execution/gradle-version'
|
||||||
|
import wrapperChecksums from './wrapper-validation/wrapper-checksums.json'
|
||||||
|
|
||||||
|
const FEATURE_LIFECYCLE_DOC = 'https://docs.gradle.org/current/userguide/feature_lifecycle.html'
|
||||||
|
|
||||||
|
const LATEST_RELEASED_MAJOR = determineLatestReleasedMajor(wrapperChecksums.map(entry => entry.version))
|
||||||
|
const LATEST_MAJOR_VERSION_ENV = 'GRADLE_ACTIONS_LATEST_GRADLE_MAJOR'
|
||||||
|
|
||||||
|
export function determineLatestReleasedMajor(versions: string[]): number | undefined {
|
||||||
|
const releasedMajors = versions
|
||||||
|
.map(version => new GradleVersion(version))
|
||||||
|
.filter(parsed => parsed.isFinalRelease())
|
||||||
|
.map(parsed => parsed.major)
|
||||||
|
return releasedMajors.length > 0 ? Math.max(...releasedMajors) : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exportLatestReleasedMajor(): void {
|
||||||
|
if (LATEST_RELEASED_MAJOR !== undefined) {
|
||||||
|
core.exportVariable(LATEST_MAJOR_VERSION_ENV, LATEST_RELEASED_MAJOR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reportSupportStatus(buildResults: BuildResult[]): void {
|
||||||
|
if (LATEST_RELEASED_MAJOR === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusByVersion = new Map<string, string>()
|
||||||
|
for (const {gradleVersion, versionStatus} of buildResults) {
|
||||||
|
if (versionStatus) {
|
||||||
|
statusByVersion.set(gradleVersion, versionStatus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [gradleVersion, status] of statusByVersion) {
|
||||||
|
const version = new GradleVersion(gradleVersion)
|
||||||
|
switch (status) {
|
||||||
|
case 'eol':
|
||||||
|
core.warning(eolMessage(version, LATEST_RELEASED_MAJOR), {title: 'Gradle version at end-of-life'})
|
||||||
|
break
|
||||||
|
case 'maintenance':
|
||||||
|
core.notice(maintenanceMessage(version, LATEST_RELEASED_MAJOR), {
|
||||||
|
title: 'Gradle version in maintenance'
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function eolMessage(version: GradleVersion, latestMajor: number): string {
|
||||||
|
return `Gradle ${version.version} has reached end-of-life: the ${version.major}.x release line no longer receives bug fixes or security fixes. Gradle ${latestMajor}.x is the current release line, and upgrading is recommended. See ${FEATURE_LIFECYCLE_DOC}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function maintenanceMessage(version: GradleVersion, latestMajor: number): string {
|
||||||
|
return `Gradle ${version.version} is in maintenance-only support: the ${version.major}.x release line receives critical bug fixes and security fixes only, and reaches end-of-life when Gradle ${latestMajor + 1} is released. Consider upgrading to Gradle ${latestMajor}.x. See ${FEATURE_LIFECYCLE_DOC}`
|
||||||
|
}
|
||||||
+18
@@ -15,6 +15,7 @@ settingsEvaluated { settings ->
|
|||||||
spec.getParameters().getRequestedTasks().set(gradle.startParameter.taskNames.join(" "))
|
spec.getParameters().getRequestedTasks().set(gradle.startParameter.taskNames.join(" "))
|
||||||
spec.getParameters().getGradleHomeDir().set(gradle.gradleHomeDir.absolutePath)
|
spec.getParameters().getGradleHomeDir().set(gradle.gradleHomeDir.absolutePath)
|
||||||
spec.getParameters().getInvocationId().set(gradle.ext.invocationId)
|
spec.getParameters().getInvocationId().set(gradle.ext.invocationId)
|
||||||
|
spec.getParameters().getVersionStatus().set(gradle.ext.gradleVersionStatus ?: "")
|
||||||
})
|
})
|
||||||
|
|
||||||
gradle.services.get(BuildEventListenerRegistryInternal).onOperationCompletion(projectTracker)
|
gradle.services.get(BuildEventListenerRegistryInternal).onOperationCompletion(projectTracker)
|
||||||
@@ -30,6 +31,7 @@ abstract class BuildResultsRecorder implements BuildService<BuildResultsRecorder
|
|||||||
Property<String> getRequestedTasks()
|
Property<String> getRequestedTasks()
|
||||||
Property<String> getGradleHomeDir()
|
Property<String> getGradleHomeDir()
|
||||||
Property<String> getInvocationId()
|
Property<String> getInvocationId()
|
||||||
|
Property<String> getVersionStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
void started(BuildOperationDescriptor buildOperation, OperationStartEvent startEvent) {}
|
void started(BuildOperationDescriptor buildOperation, OperationStartEvent startEvent) {}
|
||||||
@@ -49,6 +51,16 @@ abstract class BuildResultsRecorder implements BuildService<BuildResultsRecorder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void setStepOutput(String name, String value) {
|
||||||
|
def githubOutput = System.getenv("GITHUB_OUTPUT")
|
||||||
|
if (githubOutput) {
|
||||||
|
new File(githubOutput) << "${name}=${value}\n"
|
||||||
|
} else {
|
||||||
|
// Retained for compatibility with older GHES versions
|
||||||
|
println("::set-output name=${name}::${value}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
def buildResults = [
|
def buildResults = [
|
||||||
@@ -56,11 +68,17 @@ abstract class BuildResultsRecorder implements BuildService<BuildResultsRecorder
|
|||||||
rootProjectDir: getParameters().getRootProjectDir().get(),
|
rootProjectDir: getParameters().getRootProjectDir().get(),
|
||||||
requestedTasks: getParameters().getRequestedTasks().get(),
|
requestedTasks: getParameters().getRequestedTasks().get(),
|
||||||
gradleVersion: GradleVersion.current().version,
|
gradleVersion: GradleVersion.current().version,
|
||||||
|
versionStatus: getParameters().getVersionStatus().getOrElse("") ?: null,
|
||||||
gradleHomeDir: getParameters().getGradleHomeDir().get(),
|
gradleHomeDir: getParameters().getGradleHomeDir().get(),
|
||||||
buildFailed: buildFailed,
|
buildFailed: buildFailed,
|
||||||
configCacheHit: configCacheHit
|
configCacheHit: configCacheHit
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def versionStatus = getParameters().getVersionStatus().getOrElse("")
|
||||||
|
if (versionStatus) {
|
||||||
|
setStepOutput("gradle-version-status", versionStatus)
|
||||||
|
}
|
||||||
|
|
||||||
def runnerTempDir = System.getProperty("RUNNER_TEMP") ?: System.getenv("RUNNER_TEMP")
|
def runnerTempDir = System.getProperty("RUNNER_TEMP") ?: System.getenv("RUNNER_TEMP")
|
||||||
def githubActionStep = System.getProperty("GITHUB_ACTION") ?: System.getenv("GITHUB_ACTION")
|
def githubActionStep = System.getProperty("GITHUB_ACTION") ?: System.getenv("GITHUB_ACTION")
|
||||||
if (!runnerTempDir || !githubActionStep) {
|
if (!runnerTempDir || !githubActionStep) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.gradle.util.GradleVersion
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
def SKIP_BUILD_CAPTURE = "GRADLE_ACTIONS_SKIP_BUILD_RESULT_CAPTURE"
|
def SKIP_BUILD_CAPTURE = "GRADLE_ACTIONS_SKIP_BUILD_RESULT_CAPTURE"
|
||||||
|
def LATEST_GRADLE_MAJOR_ENV = "GRADLE_ACTIONS_LATEST_GRADLE_MAJOR"
|
||||||
def BUILD_SCAN_PLUGIN_ID = "com.gradle.build-scan"
|
def BUILD_SCAN_PLUGIN_ID = "com.gradle.build-scan"
|
||||||
def BUILD_SCAN_EXTENSION = "buildScan"
|
def BUILD_SCAN_EXTENSION = "buildScan"
|
||||||
def DEVELOCITY_PLUGIN_ID = "com.gradle.develocity"
|
def DEVELOCITY_PLUGIN_ID = "com.gradle.develocity"
|
||||||
@@ -23,6 +24,8 @@ if (isTopLevelBuild) {
|
|||||||
def resultsWriter = new ResultsWriter()
|
def resultsWriter = new ResultsWriter()
|
||||||
def version = GradleVersion.current().baseVersion
|
def version = GradleVersion.current().baseVersion
|
||||||
|
|
||||||
|
gradle.ext.gradleVersionStatus = supportStatusOf(version, System.getenv(LATEST_GRADLE_MAJOR_ENV))
|
||||||
|
|
||||||
def atLeastGradle3 = version >= GradleVersion.version("3.0")
|
def atLeastGradle3 = version >= GradleVersion.version("3.0")
|
||||||
def atLeastGradle6 = version >= GradleVersion.version("6.0")
|
def atLeastGradle6 = version >= GradleVersion.version("6.0")
|
||||||
def atLeastGradle7 = version >= GradleVersion.version("7.0")
|
def atLeastGradle7 = version >= GradleVersion.version("7.0")
|
||||||
@@ -84,13 +87,46 @@ def captureUsingBuildService(invocationId) {
|
|||||||
apply from: 'gradle-actions.build-result-capture-service.plugin.groovy'
|
apply from: 'gradle-actions.build-result-capture-service.plugin.groovy'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String supportStatusOf(version, String latestGradleMajor) {
|
||||||
|
def latestMajor = latestGradleMajor?.find(/^\d+$/)?.toInteger()
|
||||||
|
def currentMajor = version.version.find(/^\d+/)?.toInteger()
|
||||||
|
if (!latestMajor || !currentMajor) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (Math.max(0, latestMajor - currentMajor)) {
|
||||||
|
case 0:
|
||||||
|
return "active"
|
||||||
|
case 1:
|
||||||
|
return "maintenance"
|
||||||
|
default:
|
||||||
|
return "eol"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStepOutput(String name, String value) {
|
||||||
|
def githubOutput = System.getenv("GITHUB_OUTPUT")
|
||||||
|
if (githubOutput) {
|
||||||
|
new File(githubOutput) << "${name}=${value}\n"
|
||||||
|
} else {
|
||||||
|
// Retained for compatibility with older GHES versions
|
||||||
|
println("::set-output name=${name}::${value}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void captureUsingBuildFinished(gradle, String invocationId, ResultsWriter resultsWriter) {
|
void captureUsingBuildFinished(gradle, String invocationId, ResultsWriter resultsWriter) {
|
||||||
gradle.buildFinished { result ->
|
gradle.buildFinished { result ->
|
||||||
|
def versionStatus = gradle.ext.gradleVersionStatus
|
||||||
|
if (versionStatus) {
|
||||||
|
setStepOutput("gradle-version-status", versionStatus)
|
||||||
|
}
|
||||||
|
|
||||||
def buildResults = [
|
def buildResults = [
|
||||||
rootProjectName: rootProject.name,
|
rootProjectName: rootProject.name,
|
||||||
rootProjectDir: rootProject.projectDir.absolutePath,
|
rootProjectDir: rootProject.projectDir.absolutePath,
|
||||||
requestedTasks: gradle.startParameter.taskNames.join(" "),
|
requestedTasks: gradle.startParameter.taskNames.join(" "),
|
||||||
gradleVersion: GradleVersion.current().version,
|
gradleVersion: GradleVersion.current().version,
|
||||||
|
versionStatus: versionStatus,
|
||||||
gradleHomeDir: gradle.gradleHomeDir.absolutePath,
|
gradleHomeDir: gradle.gradleHomeDir.absolutePath,
|
||||||
buildFailed: result.failure != null,
|
buildFailed: result.failure != null,
|
||||||
configCacheHit: false
|
configCacheHit: false
|
||||||
@@ -110,13 +146,7 @@ void captureUsingBuildScanPublished(buildScanExtension, String invocationId, Res
|
|||||||
]
|
]
|
||||||
resultsWriter.writeToResultsFile("build-scans", invocationId, scanResults)
|
resultsWriter.writeToResultsFile("build-scans", invocationId, scanResults)
|
||||||
|
|
||||||
def githubOutput = System.getenv("GITHUB_OUTPUT")
|
setStepOutput("build-scan-url", buildScan.buildScanUri.toString())
|
||||||
if (githubOutput) {
|
|
||||||
new File(githubOutput) << "build-scan-url=${buildScan.buildScanUri}\n"
|
|
||||||
} else {
|
|
||||||
// Retained for compatibility with older GHES versions
|
|
||||||
println("::set-output name=build-scan-url::${buildScan.buildScanUri}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onError { error ->
|
onError { error ->
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
} from './configuration'
|
} from './configuration'
|
||||||
import * as wrapperValidator from './wrapper-validation/wrapper-validator'
|
import * as wrapperValidator from './wrapper-validation/wrapper-validator'
|
||||||
import {initializeGradleUserHome} from './gradle-user-home'
|
import {initializeGradleUserHome} from './gradle-user-home'
|
||||||
|
import {exportLatestReleasedMajor, reportSupportStatus} from './gradle-support-status'
|
||||||
|
|
||||||
const GRADLE_SETUP_VAR = 'GRADLE_BUILD_ACTION_SETUP_COMPLETED'
|
const GRADLE_SETUP_VAR = 'GRADLE_BUILD_ACTION_SETUP_COMPLETED'
|
||||||
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
|
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
|
||||||
@@ -46,6 +47,7 @@ export async function setup(
|
|||||||
core.saveState(GRADLE_USER_HOME, gradleUserHome)
|
core.saveState(GRADLE_USER_HOME, gradleUserHome)
|
||||||
|
|
||||||
initializeGradleUserHome(userHome, gradleUserHome, cacheConfig.getCacheEncryptionKey())
|
initializeGradleUserHome(userHome, gradleUserHome, cacheConfig.getCacheEncryptionKey())
|
||||||
|
exportLatestReleasedMajor()
|
||||||
|
|
||||||
// Exchange the long-lived access key(s) for short-lived tokens, resolving the token for the
|
// Exchange the long-lived access key(s) for short-lived tokens, resolving the token for the
|
||||||
// configured Develocity server and retaining it for the post-action (save) step.
|
// configured Develocity server and retaining it for the post-action (save) step.
|
||||||
@@ -89,6 +91,8 @@ export async function complete(
|
|||||||
|
|
||||||
markBuildResultsProcessed()
|
markBuildResultsProcessed()
|
||||||
|
|
||||||
|
reportSupportStatus(buildResults)
|
||||||
|
|
||||||
core.info('Completed post-action step')
|
core.info('Completed post-action step')
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
+88
-3
@@ -6,6 +6,7 @@ import static org.junit.Assume.assumeTrue
|
|||||||
|
|
||||||
class TestBuildResultRecorder extends BaseInitScriptTest {
|
class TestBuildResultRecorder extends BaseInitScriptTest {
|
||||||
def initScript = 'gradle-actions.build-result-capture.init.gradle'
|
def initScript = 'gradle-actions.build-result-capture.init.gradle'
|
||||||
|
String latestGradleMajor
|
||||||
|
|
||||||
def "produces build results file for build with #testGradleVersion"() {
|
def "produces build results file for build with #testGradleVersion"() {
|
||||||
assumeTrue testGradleVersion.compatibleWithCurrentJvm
|
assumeTrue testGradleVersion.compatibleWithCurrentJvm
|
||||||
@@ -269,6 +270,63 @@ task expectFailure {
|
|||||||
testGradleVersion << SETTINGS_PLUGIN_VERSIONS
|
testGradleVersion << SETTINGS_PLUGIN_VERSIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "captures version status '#expectedStatus' when the latest released major is #latestMajor"() {
|
||||||
|
assumeTrue GRADLE_8_X.compatibleWithCurrentJvm
|
||||||
|
|
||||||
|
when:
|
||||||
|
latestGradleMajor = latestMajor
|
||||||
|
run(GRADLE_8_X.gradleVersion)
|
||||||
|
|
||||||
|
then:
|
||||||
|
assertVersionStatus(expectedStatus)
|
||||||
|
|
||||||
|
where:
|
||||||
|
latestMajor | expectedStatus
|
||||||
|
'7' | 'active'
|
||||||
|
'8' | 'active'
|
||||||
|
'9' | 'maintenance'
|
||||||
|
'10' | 'eol'
|
||||||
|
'13' | 'eol'
|
||||||
|
}
|
||||||
|
|
||||||
|
def "captures version status for #testGradleVersion"() {
|
||||||
|
assumeTrue testGradleVersion.compatibleWithCurrentJvm
|
||||||
|
|
||||||
|
when:
|
||||||
|
latestGradleMajor = '9'
|
||||||
|
run(testGradleVersion.gradleVersion)
|
||||||
|
|
||||||
|
then:
|
||||||
|
assertVersionStatus(expectedStatus)
|
||||||
|
|
||||||
|
where:
|
||||||
|
testGradleVersion | expectedStatus
|
||||||
|
GRADLE_6_X | 'eol'
|
||||||
|
GRADLE_7_X | 'eol'
|
||||||
|
GRADLE_8_X | 'maintenance'
|
||||||
|
}
|
||||||
|
|
||||||
|
def "captures no version status when the action did not supply the latest released major"() {
|
||||||
|
assumeTrue GRADLE_8_X.compatibleWithCurrentJvm
|
||||||
|
|
||||||
|
when:
|
||||||
|
run(GRADLE_8_X.gradleVersion)
|
||||||
|
|
||||||
|
then:
|
||||||
|
assertVersionStatus(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
def "captures no version status when the latest released major is not a number"() {
|
||||||
|
assumeTrue GRADLE_8_X.compatibleWithCurrentJvm
|
||||||
|
|
||||||
|
when:
|
||||||
|
latestGradleMajor = 'not-a-number'
|
||||||
|
run(GRADLE_8_X.gradleVersion)
|
||||||
|
|
||||||
|
then:
|
||||||
|
assertVersionStatus(null)
|
||||||
|
}
|
||||||
|
|
||||||
def run(def args = ['help'], def gradleVersion) {
|
def run(def args = ['help'], def gradleVersion) {
|
||||||
return run(args, initScript, gradleVersion, jvmArgs, envVars)
|
return run(args, initScript, gradleVersion, jvmArgs, envVars)
|
||||||
}
|
}
|
||||||
@@ -278,17 +336,26 @@ task expectFailure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def getJvmArgs() {
|
def getJvmArgs() {
|
||||||
[
|
def jvmArgs = [
|
||||||
"-DRUNNER_TEMP=${testProjectDir.absolutePath}".toString(),
|
"-DRUNNER_TEMP=${testProjectDir.absolutePath}".toString(),
|
||||||
"-DGITHUB_ACTION=github-step-id".toString()
|
"-DGITHUB_ACTION=github-step-id".toString()
|
||||||
]
|
]
|
||||||
|
if (latestGradleMajor != null) {
|
||||||
|
jvmArgs << "-DGRADLE_ACTIONS_LATEST_GRADLE_MAJOR=${latestGradleMajor}".toString()
|
||||||
|
}
|
||||||
|
jvmArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
def getEnvVars() {
|
def getEnvVars() {
|
||||||
[
|
def envVars = [
|
||||||
RUNNER_TEMP: testProjectDir.absolutePath,
|
RUNNER_TEMP: testProjectDir.absolutePath,
|
||||||
GITHUB_ACTION: 'github-step-id'
|
GITHUB_ACTION: 'github-step-id',
|
||||||
|
GITHUB_OUTPUT: githubOutputFile.absolutePath
|
||||||
]
|
]
|
||||||
|
if (latestGradleMajor != null) {
|
||||||
|
envVars.GRADLE_ACTIONS_LATEST_GRADLE_MAJOR = latestGradleMajor
|
||||||
|
}
|
||||||
|
envVars
|
||||||
}
|
}
|
||||||
|
|
||||||
void assertResults(String task, TestGradleVersion testGradleVersion, boolean hasFailure, boolean configCacheHit = false) {
|
void assertResults(String task, TestGradleVersion testGradleVersion, boolean hasFailure, boolean configCacheHit = false) {
|
||||||
@@ -308,6 +375,24 @@ task expectFailure {
|
|||||||
assert scanResults['buildScanFailed'] == scanUploadFailed
|
assert scanResults['buildScanFailed'] == scanUploadFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void assertVersionStatus(String expectedStatus) {
|
||||||
|
def results = new JsonSlurper().parse(buildResultFile)
|
||||||
|
assert results['versionStatus'] == expectedStatus
|
||||||
|
if (expectedStatus == null) {
|
||||||
|
assert !githubOutput.contains('gradle-version-status')
|
||||||
|
} else {
|
||||||
|
assert githubOutput.contains("gradle-version-status=${expectedStatus}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getGithubOutputFile() {
|
||||||
|
new File(testProjectDir, 'github-output')
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getGithubOutput() {
|
||||||
|
githubOutputFile.exists() ? githubOutputFile.text : ''
|
||||||
|
}
|
||||||
|
|
||||||
private File getBuildResultFile() {
|
private File getBuildResultFile() {
|
||||||
def buildResultsDir = new File(testProjectDir, '.gradle-actions/build-results')
|
def buildResultsDir = new File(testProjectDir, '.gradle-actions/build-results')
|
||||||
assert buildResultsDir.directory
|
assert buildResultsDir.directory
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import {beforeEach, describe, expect, it, jest} from '@jest/globals'
|
||||||
|
|
||||||
|
import {BuildResult} from '../../src/build-results'
|
||||||
|
|
||||||
|
// Mock @actions/core
|
||||||
|
const mockWarning = jest.fn<(message: string, properties?: {title?: string}) => void>()
|
||||||
|
const mockNotice = jest.fn<(message: string, properties?: {title?: string}) => void>()
|
||||||
|
const mockExportVariable = jest.fn<(name: string, value: string | number) => void>()
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
warning: mockWarning,
|
||||||
|
notice: mockNotice,
|
||||||
|
exportVariable: mockExportVariable
|
||||||
|
}))
|
||||||
|
|
||||||
|
const {determineLatestReleasedMajor, exportLatestReleasedMajor, reportSupportStatus} =
|
||||||
|
await import('../../src/gradle-support-status')
|
||||||
|
|
||||||
|
function build(gradleVersion: string, versionStatus?: string): BuildResult {
|
||||||
|
return {gradleVersion, versionStatus} as BuildResult
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('determineLatestReleasedMajor', () => {
|
||||||
|
it('ignores pre-releases and snapshots of an unreleased major', () => {
|
||||||
|
const versions = [
|
||||||
|
'10.0.0-milestone-1',
|
||||||
|
'10.0.0-rc-1',
|
||||||
|
'10.0.0-SNAPSHOT',
|
||||||
|
'10.0.0-20260101120000+0000',
|
||||||
|
'9.6.1',
|
||||||
|
'8.14'
|
||||||
|
]
|
||||||
|
|
||||||
|
expect(determineLatestReleasedMajor(versions)).toBe(9)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('is unaffected by the ordering of the version list', () => {
|
||||||
|
expect(determineLatestReleasedMajor(['1.0', '9.0.0', '4.10.3'])).toBe(9)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('is undefined when no final release is present', () => {
|
||||||
|
expect(determineLatestReleasedMajor(['9.0.0-rc-1', '10.0.0-SNAPSHOT'])).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('is undefined for an empty version list', () => {
|
||||||
|
expect(determineLatestReleasedMajor([])).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('exportLatestReleasedMajor', () => {
|
||||||
|
it('exports the boundary the init script needs to classify the running version', () => {
|
||||||
|
exportLatestReleasedMajor()
|
||||||
|
|
||||||
|
expect(mockExportVariable).toHaveBeenCalledWith('GRADLE_ACTIONS_LATEST_GRADLE_MAJOR', expect.any(Number))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('reportSupportStatus', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('warns about a version recorded as end-of-life', () => {
|
||||||
|
reportSupportStatus([build('7.6.4', 'eol')])
|
||||||
|
|
||||||
|
expect(mockNotice).not.toHaveBeenCalled()
|
||||||
|
expect(mockWarning).toHaveBeenCalledTimes(1)
|
||||||
|
const [message, properties] = mockWarning.mock.calls[0]
|
||||||
|
expect(message).toContain('Gradle 7.6.4 has reached end-of-life')
|
||||||
|
expect(message).toContain('the 7.x release line')
|
||||||
|
expect(properties?.title).toBe('Gradle version at end-of-life')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('notices a version recorded as maintenance-only', () => {
|
||||||
|
reportSupportStatus([build('8.14', 'maintenance')])
|
||||||
|
|
||||||
|
expect(mockWarning).not.toHaveBeenCalled()
|
||||||
|
expect(mockNotice).toHaveBeenCalledTimes(1)
|
||||||
|
const [message, properties] = mockNotice.mock.calls[0]
|
||||||
|
expect(message).toContain('Gradle 8.14 is in maintenance-only support')
|
||||||
|
expect(properties?.title).toBe('Gradle version in maintenance')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('says nothing about a version recorded as active', () => {
|
||||||
|
reportSupportStatus([build('9.6.1', 'active')])
|
||||||
|
|
||||||
|
expect(mockWarning).not.toHaveBeenCalled()
|
||||||
|
expect(mockNotice).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('says nothing when the init script recorded no status', () => {
|
||||||
|
reportSupportStatus([build('7.6.4')])
|
||||||
|
|
||||||
|
expect(mockWarning).not.toHaveBeenCalled()
|
||||||
|
expect(mockNotice).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('annotates each distinct version once', () => {
|
||||||
|
reportSupportStatus([build('7.6.4', 'eol'), build('4.10.3', 'eol'), build('7.6.4', 'eol')])
|
||||||
|
|
||||||
|
expect(mockWarning).toHaveBeenCalledTimes(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('says nothing when no Gradle build ran', () => {
|
||||||
|
reportSupportStatus([])
|
||||||
|
|
||||||
|
expect(mockWarning).not.toHaveBeenCalled()
|
||||||
|
expect(mockNotice).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user