diff --git a/lib/modules/manager/maven/__fixtures__/minimum_snapshot.pom.xml b/lib/modules/manager/maven/__fixtures__/minimum_snapshot.pom.xml new file mode 100644 index 00000000000000..37ffe02f8e3523 --- /dev/null +++ b/lib/modules/manager/maven/__fixtures__/minimum_snapshot.pom.xml @@ -0,0 +1,6 @@ + + 4.0.0 + com.mycompany.app + my-app + 0.0.1-SNAPSHOT + diff --git a/lib/modules/manager/maven/extract.spec.ts b/lib/modules/manager/maven/extract.spec.ts index 4c8fc2ab43077e..3ff8e84aaec9d0 100644 --- a/lib/modules/manager/maven/extract.spec.ts +++ b/lib/modules/manager/maven/extract.spec.ts @@ -243,6 +243,20 @@ describe('modules/manager/maven/extract', () => { packageFileVersion: '1', }); }); + + it('tries minimum snapshot manifests', () => { + const res = extractPackage( + Fixtures.get(`minimum_snapshot.pom.xml`), + 'some-file', + ); + expect(res).toEqual({ + datasource: 'maven', + deps: [], + mavenProps: {}, + packageFile: 'some-file', + packageFileVersion: '0.0.1-SNAPSHOT', + }); + }); }); describe('resolveParents', () => { diff --git a/lib/modules/manager/maven/update.spec.ts b/lib/modules/manager/maven/update.spec.ts index 5c8c6ab52b360d..a6f9a5717a361e 100644 --- a/lib/modules/manager/maven/update.spec.ts +++ b/lib/modules/manager/maven/update.spec.ts @@ -5,6 +5,7 @@ import { bumpPackageVersion, updateDependency } from './update'; const simpleContent = Fixtures.get(`simple.pom.xml`); const minimumContent = Fixtures.get(`minimum.pom.xml`); +const minimumSnapshotContent = Fixtures.get(`minimum_snapshot.pom.xml`); const prereleaseContent = Fixtures.get(`prerelease.pom.xml`); describe('modules/manager/maven/update', () => { @@ -30,6 +31,53 @@ describe('modules/manager/maven/update', () => { expect(project.valueWithPath('version')).toBe('0.0.2'); }); + it('bumps pom.xml version keeping SNAPSHOT', () => { + const { bumpedContent } = bumpPackageVersion( + minimumSnapshotContent, + '0.0.1-SNAPSHOT', + 'patch', + ); + + const project = new XmlDocument(bumpedContent!); + expect(project.valueWithPath('version')).toBe('0.0.2-SNAPSHOT'); + }); + + it('bumps pom.xml minor version keeping SNAPSHOT', () => { + const { bumpedContent } = bumpPackageVersion( + minimumSnapshotContent, + '0.0.1-SNAPSHOT', + 'minor', + ); + + const project = new XmlDocument(bumpedContent!); + expect(project.valueWithPath('version')).toBe('0.1.0-SNAPSHOT'); + }); + + it('bumps pom.xml major version keeping SNAPSHOT', () => { + const { bumpedContent } = bumpPackageVersion( + minimumSnapshotContent, + '0.0.1-SNAPSHOT', + 'major', + ); + + const project = new XmlDocument(bumpedContent!); + expect(project.valueWithPath('version')).toBe('1.0.0-SNAPSHOT'); + }); + + it('bumps pom.xml version keeping qualifier with -SNAPSHOT', () => { + const { bumpedContent } = bumpPackageVersion( + minimumSnapshotContent.replace( + '0.0.1-SNAPSHOT', + '0.0.1-qualified-SNAPSHOT', + ), + '0.0.1-qualified-SNAPSHOT', + 'patch', + ); + + const project = new XmlDocument(bumpedContent!); + expect(project.valueWithPath('version')).toBe('0.0.2-qualified-SNAPSHOT'); + }); + it('does not bump version twice', () => { const { bumpedContent } = bumpPackageVersion( simpleContent, @@ -71,6 +119,17 @@ describe('modules/manager/maven/update', () => { expect(bumpedContent).toEqual(simpleContent); }); + it('bumps pom.xml version to SNAPSHOT with prerelease', () => { + const { bumpedContent } = bumpPackageVersion( + simpleContent, + '0.0.1', + 'prerelease', + ); + + const project = new XmlDocument(bumpedContent!); + expect(project.valueWithPath('version')).toBe('0.0.2-SNAPSHOT'); + }); + it('bumps pom.xml version with prerelease semver level', () => { const { bumpedContent } = bumpPackageVersion( prereleaseContent, diff --git a/lib/modules/manager/maven/update.ts b/lib/modules/manager/maven/update.ts index 055d033cccb9ac..1d89f30f5c3ed7 100644 --- a/lib/modules/manager/maven/update.ts +++ b/lib/modules/manager/maven/update.ts @@ -1,3 +1,4 @@ +import is from '@sindresorhus/is'; import semver, { ReleaseType } from 'semver'; import { XmlDocument } from 'xmldoc'; import { logger } from '../../../logger'; @@ -78,7 +79,31 @@ export function bumpPackageVersion( const startTagPosition = versionNode.startTagPosition; const versionPosition = content.indexOf(versionNode.val, startTagPosition); - const newPomVersion = semver.inc(currentValue, bumpVersion); + let newPomVersion: string | null = null; + const currentPrereleaseValue = semver.prerelease(currentValue); + if (isSnapshot(currentPrereleaseValue)) { + // It is already a SNAPSHOT version. + // Therefore the same qualifier (prerelease) will be used as before. + let releaseType = bumpVersion; + if (!bumpVersion.startsWith('pre')) { + releaseType = `pre${bumpVersion}` as ReleaseType; + } + newPomVersion = semver.inc( + currentValue, + releaseType, + currentPrereleaseValue!.join('.'), + false, + ); + } else if (currentPrereleaseValue) { + // Some qualifier which is not a SNAPSHOT is present. + // The expected behaviour in this case is unclear and the standard increase will be used. + newPomVersion = semver.inc(currentValue, bumpVersion); + } else { + // A release version without any qualifier is present. + // Therefore the SNAPSHOT qualifier will be added if a prerelease is requested. + // This will do a normal increment, ignoring SNAPSHOT, if a non-prerelease bumpVersion is configured + newPomVersion = semver.inc(currentValue, bumpVersion, 'SNAPSHOT', false); + } if (!newPomVersion) { throw new Error('semver inc failed'); } @@ -108,3 +133,10 @@ export function bumpPackageVersion( } return { bumpedContent }; } + +function isSnapshot( + prerelease: ReadonlyArray | null, +): boolean { + const lastPart = prerelease?.at(-1); + return is.string(lastPart) && lastPart.endsWith('SNAPSHOT'); +}