Report patches available for version

This commit is contained in:
Vlad Chesnokov
2026-08-26 15:46:04 +02:00
parent b24d6c9b7e
commit a9692ad11b
5 changed files with 220 additions and 84 deletions
+61 -44
View File
@@ -8,54 +8,58 @@ jest.unstable_mockModule('@actions/core', () => ({
notice: mockNotice
}))
const {determineLatestReleasedMajor, getSupportStatus, reportSupportStatus} =
await import('../../src/gradle-support-status')
const {GradleVersion} = await import('../../src/execution/gradle-version')
import wrapperChecksums from '../../src/wrapper-validation/wrapper-checksums.json'
const {SupportStatusKind, reportSupportStatusUsing, supportStatusUsing} = await import(
'../../src/gradle-support-status'
)
const latestReleasedMajor = determineLatestReleasedMajor(wrapperChecksums.map(entry => entry.version))
const MAINTENANCE_VERSION = `${(latestReleasedMajor ?? 0) - 1}.0`
const RELEASED = ['10.4.2', '10.1.0', '10.0.0', '9.6.1', '9.0.0', '8.14', '1.0']
const DOC = 'https://docs.gradle.org/current/userguide/feature_lifecycle.html#eol_support'
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('getSupportStatus', () => {
it.each(['10.0.0', '10.4.2', '11.0.0-milestone-1', '12.0.0'])('treats %s as active', version => {
expect(getSupportStatus(new GradleVersion(version), 10)).toBe('active')
describe('supportStatusOf', () => {
it.each(['10.0.0', '10.4.2', '11.0.0-milestone-1'])('treats %s as active', version => {
expect(supportStatusUsing(version, RELEASED)).toEqual({kind: SupportStatusKind.Active})
})
it.each(['9.0.0', '9.6.1', '9.7.0-rc-2'])('treats %s as maintenance-only', version => {
expect(getSupportStatus(new GradleVersion(version), 10)).toBe('maintenance')
expect(supportStatusUsing(version, RELEASED)).toEqual({kind: SupportStatusKind.Maintenance})
})
it.each(['8.14', '8.0.2', '7.6.4', '4.10.3', '1.0'])('treats %s as end-of-life', version => {
expect(getSupportStatus(new GradleVersion(version), 10)).toBe('eol')
expect(supportStatusUsing(version, RELEASED)).toEqual({kind: SupportStatusKind.Eol})
})
it('reports a newer patch in the latest release line instead of active', () => {
expect(supportStatusUsing('10.0.0', ['10.1.0', '10.0.1', '10.0.0'])).toEqual({
kind: SupportStatusKind.PatchAvailable,
newerPatch: '10.0.1'
})
})
it('reports the newest patch when several are available', () => {
expect(supportStatusUsing('8.14', ['8.14.5', '8.14.2', '8.14'])).toEqual({
kind: SupportStatusKind.PatchAvailable,
newerPatch: '8.14.5'
})
})
it('ignores a newer minor in the same major', () => {
expect(supportStatusUsing('9.3.0', ['9.4.0', '9.3.0'])).toEqual({kind: SupportStatusKind.Active})
})
it('ignores a newer patch when the release line is already outdated', () => {
const released = ['10.0.0', '9.0.1', '9.0.0', '8.0.1', '8.0']
expect(supportStatusUsing('9.0.0', released)).toEqual({kind: SupportStatusKind.Maintenance})
expect(supportStatusUsing('8.0', released)).toEqual({kind: SupportStatusKind.Eol})
})
it('reports no patch update for a pre-release', () => {
expect(supportStatusUsing('9.3.0-rc-1', ['9.3.1', '9.3.0'])).toEqual({kind: SupportStatusKind.Active})
})
it('treats every version as active when the release data holds no final release', () => {
expect(supportStatusUsing('1.0', ['9.0.0-rc-1', '10.0.0-SNAPSHOT'])).toEqual({kind: SupportStatusKind.Active})
})
})
@@ -65,43 +69,56 @@ describe('reportSupportStatus', () => {
})
it('warns about an end-of-life version', () => {
reportSupportStatus(['7.6.4'])
reportSupportStatusUsing(['7.6.4'], RELEASED)
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 no longer receives bug fixes or security fixes')
expect(message).toContain('Gradle 10.x is the current release line')
expect(message).toContain(DOC)
expect(properties?.title).toBe('Gradle version at end-of-life')
})
it('notices a maintenance-only version', () => {
reportSupportStatus([MAINTENANCE_VERSION])
reportSupportStatusUsing(['9.0.0'], RELEASED)
expect(mockWarning).not.toHaveBeenCalled()
expect(mockNotice).toHaveBeenCalledTimes(1)
const [message, properties] = mockNotice.mock.calls[0]
expect(message).toContain(`Gradle ${MAINTENANCE_VERSION} is in maintenance-only support`)
expect(message).toContain('Gradle 9.0.0 is in maintenance-only support')
expect(message).toContain('receives critical bug fixes and security fixes only')
expect(message).toContain('reaches end-of-life when Gradle 11 is released')
expect(properties?.title).toBe('Gradle version in maintenance')
})
it('notices a version with a newer patch available', () => {
reportSupportStatusUsing(['10.0.0'], ['10.0.1', '10.0.0'])
expect(mockWarning).not.toHaveBeenCalled()
expect(mockNotice).toHaveBeenCalledTimes(1)
const [message, properties] = mockNotice.mock.calls[0]
expect(message).toContain('Gradle 10.0.0 is not the latest patch release')
expect(message).toContain('Gradle 10.0.1 is available')
expect(properties?.title).toBe('Gradle patch update available')
})
it('says nothing about a version in the latest release line', () => {
reportSupportStatus(['999.0.0'])
reportSupportStatusUsing(['10.4.2'], RELEASED)
expect(mockWarning).not.toHaveBeenCalled()
expect(mockNotice).not.toHaveBeenCalled()
})
it('annotates each distinct version once', () => {
reportSupportStatus(['7.6.4', '4.10.3', '7.6.4'])
reportSupportStatusUsing(['7.6.4', '4.10.3', '7.6.4'], RELEASED)
expect(mockWarning).toHaveBeenCalledTimes(2)
})
it('says nothing when no Gradle build ran', () => {
reportSupportStatus([])
reportSupportStatusUsing([], RELEASED)
expect(mockWarning).not.toHaveBeenCalled()
expect(mockNotice).not.toHaveBeenCalled()
+22 -13
View File
@@ -3,18 +3,22 @@ import * as github from '@actions/github'
import {afterEach, beforeEach, describe, expect, it, jest} from '@jest/globals'
import {BuildResult} from '../../src/build-results'
import {SupportStatus} from '../../src/gradle-support-status'
const mockSupportStatusOf = jest.fn<(gradleVersion: string) => SupportStatus | undefined>()
jest.unstable_mockModule('../../src/gradle-support-status', () => ({
supportStatusOf: mockSupportStatusOf
let releasedVersions = [{version: '8.0', checksum: ''}]
jest.unstable_mockModule('../../src/wrapper-validation/wrapper-checksums.json', () => ({
get default() {
return releasedVersions
}
}))
const {jobMarker, renderSummaryTable} = await import('../../src/job-summary')
beforeEach(() => {
mockSupportStatusOf.mockReturnValue('active')
})
async function renderWith(versions: string[], results: BuildResult[]): Promise<string> {
releasedVersions = versions.map(version => ({version, checksum: ''}))
jest.resetModules()
const {renderSummaryTable: render} = await import('../../src/job-summary')
return render(results)
}
const MATRIX_INPUT_ENV = 'INPUT_WORKFLOW-JOB-CONTEXT'
@@ -197,9 +201,8 @@ describe('renderSummaryTable', () => {
})
describe('Gradle version support status', () => {
it('marks an outdated version and adds a details section', () => {
mockSupportStatusOf.mockReturnValue('eol')
const table = renderSummaryTable([successfulHelpBuild])
it('marks an outdated version and adds a details section', async () => {
const table = await renderWith(['10.0.0', '8.0'], [successfulHelpBuild])
expect(table.trim()).toBe(dedent`
<table>
<tr>
@@ -233,14 +236,20 @@ describe('Gradle version support status', () => {
</details>
`);
})
it('marks a maintenance-only version', () => {
mockSupportStatusOf.mockReturnValue('maintenance')
const table = renderSummaryTable([successfulHelpBuild])
it('marks a maintenance-only version', async () => {
const table = await renderWith(['9.0.0', '8.0'], [successfulHelpBuild])
expect(table).toContain(
`<td align='center'>8.0 <span title="Maintenance only: receives critical bug fixes and security fixes only">:information_source:</span></td>`
)
expect(table).toContain('This Job uses an outdated Gradle version')
})
it('marks a version with a newer patch release available', async () => {
const table = await renderWith(['8.0.1', '8.0'], [successfulHelpBuild])
expect(table).toContain(
`<td align='center'>8.0 <span title="Patch update available: Gradle 8.0.1">:information_source:</span></td>`
)
expect(table).not.toContain('outdated Gradle version')
})
})
describe('jobMarker', () => {