diff --git a/lib/manager/gradle/build-gradle.spec.ts b/lib/manager/gradle/build-gradle.spec.ts index a8d0b3c5ffd4014a84f19ceb2efff1cd84017bfb..14c68fca52b9e9ea9d92d501a189a8db4f60a235 100644 --- a/lib/manager/gradle/build-gradle.spec.ts +++ b/lib/manager/gradle/build-gradle.spec.ts @@ -494,6 +494,70 @@ describe('lib/manager/gradle/updateGradleVersion', () => { ); }); + it('returns an updated file if the plugin version defined in a variable is found', () => { + const gradleFile = `val mysqlVersion = "6.0.5" + id("mysql") version mysqlVersion + `; + const updatedGradleFile = updateGradleVersion( + gradleFile, + { group: 'mysql', name: 'mysql-connector-java', version: '6.0.5' }, + '7.0.0' + ); + expect(updatedGradleFile).toEqual( + `val mysqlVersion = "7.0.0" + id("mysql") version mysqlVersion + ` + ); + }); + + it('returns an updated file if the plugin version defined in a variable in a template string without curly braces is found', () => { + const gradleFile = `val mysqlVersion = "6.0.5" + id("mysql") version "$mysqlVersion" + `; + const updatedGradleFile = updateGradleVersion( + gradleFile, + { group: 'mysql', name: 'mysql-connector-java', version: '6.0.5' }, + '7.0.0' + ); + expect(updatedGradleFile).toEqual( + `val mysqlVersion = "7.0.0" + id("mysql") version "$mysqlVersion" + ` + ); + }); + + it('returns an updated file if the plugin version defined in a variable in a template string with curly braces is found', () => { + const gradleFile = `val mysqlVersion = "6.0.5" + id("mysql") version "\${mysqlVersion}" + `; + const updatedGradleFile = updateGradleVersion( + gradleFile, + { group: 'mysql', name: 'mysql-connector-java', version: '6.0.5' }, + '7.0.0' + ); + expect(updatedGradleFile).toEqual( + `val mysqlVersion = "7.0.0" + id("mysql") version "\${mysqlVersion}" + ` + ); + }); + + it('returns an updated file if the plugin version defined in a variable in a template string with triple double quotes is found', () => { + const gradleFile = `String mysqlVersion = "6.0.5" + id 'mysql' version """$mysqlVersion""" + `; + const updatedGradleFile = updateGradleVersion( + gradleFile, + { group: 'mysql', name: 'mysql-connector-java', version: '6.0.5' }, + '7.0.0' + ); + expect(updatedGradleFile).toEqual( + `String mysqlVersion = "7.0.0" + id 'mysql' version """$mysqlVersion""" + ` + ); + }); + it('should replace a external groovy variable assigned to a specific dependency', () => { const gradleFile = 'runtime ( "mysql:mysql-connector-java:${mysqlVersion}" )'; // eslint-disable-line no-template-curly-in-string @@ -706,4 +770,23 @@ describe('lib/manager/gradle/updateGradleVersion', () => { ); expect(updatedGradleFile).toEqual('val mysqlVersion by extra { "7.0.0" }'); }); + + it('should replace a external variable assigned to a plugin dependency', () => { + const gradleFile = 'id("mysql") version "$mysqlVersion";'; + const mysqlDependency = { + group: 'mysql', + depGroup: 'mysql', + name: 'mysql-connector-java', + version: '6.0.5', + }; + collectVersionVariables([mysqlDependency], gradleFile); + + const gradleWithVersionFile = 'String mysqlVersion = "6.0.5"'; + const updatedGradleFile = updateGradleVersion( + gradleWithVersionFile, + mysqlDependency, + '7.0.0' + ); + expect(updatedGradleFile).toEqual('String mysqlVersion = "7.0.0"'); + }); }); diff --git a/lib/manager/gradle/build-gradle.ts b/lib/manager/gradle/build-gradle.ts index 78b4b22f7880818d34065d927921268f7b45609a..c9813adaabb6bc0ba4efcd09183ef073a78045a4 100644 --- a/lib/manager/gradle/build-gradle.ts +++ b/lib/manager/gradle/build-gradle.ts @@ -1,4 +1,5 @@ import { BuildDependency } from './gradle-updates-report'; +import { regEx } from '../../util/regex'; /** * Functions adapted/ported from https://github.com/patrikerdes/gradle-use-latest-versions-plugin @@ -23,10 +24,12 @@ interface UpdateFunction { } const groovyQuotes = `(?:["'](?:""|'')?)`; +const groovyVersionVariable = `(?:${groovyQuotes}\\$)?{?([^\\s"'{}$)]+)}?${groovyQuotes}?`; +const kotlinVersionVariable = `(?:"\\$)?{?([^\\s"{}$]+?)}?"?`; // https://github.com/patrikerdes/gradle-use-latest-versions-plugin/blob/8cf9c3917b8b04ba41038923cab270d2adda3aa6/src/main/groovy/se/patrikerdes/DependencyUpdate.groovy#L27-L29 function moduleStringVersionFormatMatch(dependency: GradleDependency): RegExp { - return new RegExp( + return regEx( `(${groovyQuotes}${dependency.group}:${dependency.name}:)[^$].*?(([:@].*?)?${groovyQuotes})` ); } @@ -34,17 +37,15 @@ function moduleStringVersionFormatMatch(dependency: GradleDependency): RegExp { function groovyPluginStringVersionFormatMatch( dependency: GradleDependency ): RegExp { - return new RegExp( - `(id\\s+${groovyQuotes}${dependency.group}${groovyQuotes}\\s+version\\s+${groovyQuotes})[^$].*?(${groovyQuotes})` + return regEx( + `(id\\s+${groovyQuotes}${dependency.group}${groovyQuotes}\\s+version\\s+${groovyQuotes})[^"$].*?(${groovyQuotes})` ); } function kotlinPluginStringVersionFormatMatch( dependency: GradleDependency ): RegExp { - return new RegExp( - `(id\\("${dependency.group}"\\)\\s+version\\s+")[^$].*?(")` - ); + return regEx(`(id\\("${dependency.group}"\\)\\s+version\\s+")[^$].*?(")`); } function allMapFormatOrders( @@ -62,7 +63,7 @@ function allMapFormatOrders( `${version}${comma}${group}${comma}${name}`, `${name}${comma}${version}${comma}${group}`, `${version}${comma}${name}${comma}${group}`, - ].map((regex) => new RegExp(`${prefix}${regex}${postfix}`)); + ].map((regex) => regEx(`${prefix}${regex}${postfix}`)); } function moduleMapVersionFormatMatch(dependency: GradleDependency): RegExp[] { @@ -89,7 +90,7 @@ function moduleMapVariableVersionFormatMatch( // one capture group: the version variable const group = `group\\s*:\\s*${groovyQuotes}${dependency.group}${groovyQuotes}`; const name = `name\\s*:\\s*${groovyQuotes}${dependency.name}${groovyQuotes}`; - const version = `version\\s*:\\s*(?:${groovyQuotes}\\$)?{?([^\\s"'{}$)]+)}?${groovyQuotes}?`; + const version = `version\\s*:\\s*${groovyVersionVariable}`; return allMapFormatOrders(group, name, version, '', ''); } @@ -99,14 +100,14 @@ function moduleKotlinNamedArgumentVariableVersionFormatMatch( // one capture group: the version variable const group = `group\\s*=\\s*"${dependency.group}"`; const name = `name\\s*=\\s*"${dependency.name}"`; - const version = `version\\s*=\\s*(?:"\\$)?{?([^\\s"{}$]+?)}?"?`; + const version = `version\\s*=\\s*${kotlinVersionVariable}`; return allMapFormatOrders(group, name, version, '', '[\\s),]'); } function moduleStringVariableInterpolationVersionFormatMatch( dependency: GradleDependency ): RegExp { - return new RegExp( + return regEx( `${groovyQuotes}${dependency.group}:${dependency.name}:\\$([^{].*?)${groovyQuotes}` ); } @@ -114,21 +115,37 @@ function moduleStringVariableInterpolationVersionFormatMatch( function moduleStringVariableExpressionVersionFormatMatch( dependency: GradleDependency ): RegExp { - return new RegExp( + return regEx( `${groovyQuotes}${dependency.group}:${dependency.name}:\\$` + `{([^{].*?)}${groovyQuotes}` ); } +function groovyPluginVariableVersionFormatMatch( + dependency: GradleDependency +): RegExp { + return regEx( + `id\\s+${groovyQuotes}${dependency.group}${groovyQuotes}\\s+version\\s+${groovyVersionVariable}(?:\\s|;|})` + ); +} + +function kotlinPluginVariableVersionFormatMatch( + dependency: GradleDependency +): RegExp { + return regEx( + `id\\("${dependency.group}"\\)\\s+version\\s+${kotlinVersionVariable}(?:\\s|;|})` + ); +} + function variableDefinitionFormatMatch(variable: string): RegExp { - return new RegExp(`(${variable}\\s*=\\s*?["'])(.*)(["'])`); + return regEx(`(${variable}\\s*=\\s*?["'])(.*)(["'])`); } function variableMapDefinitionFormatMatch( variable: string, version: string ): RegExp { - return new RegExp(`(${variable}\\s*:\\s*?["'])(${version})(["'])`); + return regEx(`(${variable}\\s*:\\s*?["'])(${version})(["'])`); } export function collectVersionVariables( @@ -143,6 +160,8 @@ export function collectVersionVariables( const regexes = [ moduleStringVariableExpressionVersionFormatMatch(dependency), moduleStringVariableInterpolationVersionFormatMatch(dependency), + groovyPluginVariableVersionFormatMatch(dependency), + kotlinPluginVariableVersionFormatMatch(dependency), ...moduleMapVariableVersionFormatMatch(dependency), ...moduleKotlinNamedArgumentVariableVersionFormatMatch(dependency), ]; @@ -173,8 +192,12 @@ function updateVersionLiterals( ...moduleKotlinNamedArgumentVersionFormatMatch(dependency), ]; for (const regex of regexes) { - if (regex.test(buildGradleContent)) { - return buildGradleContent.replace(regex, `$1${newVersion}$2`); + const match = regex.exec(buildGradleContent); + if (match) { + return buildGradleContent.replace( + match[0], + `${match[1]}${newVersion}${match[2]}` + ); } } return null; @@ -189,15 +212,23 @@ function updateLocalVariables( ...moduleMapVariableVersionFormatMatch(dependency), moduleStringVariableInterpolationVersionFormatMatch(dependency), moduleStringVariableExpressionVersionFormatMatch(dependency), + groovyPluginVariableVersionFormatMatch(dependency), + kotlinPluginVariableVersionFormatMatch(dependency), ...moduleKotlinNamedArgumentVariableVersionFormatMatch(dependency), ]; for (const regex of regexes) { const match = regex.exec(buildGradleContent); if (match) { - return buildGradleContent.replace( - variableDefinitionFormatMatch(match[1]), - `$1${newVersion}$3` + const variableDefinitionRegex = variableDefinitionFormatMatch(match[1]); + const variableDefinitionMatch = variableDefinitionRegex.exec( + buildGradleContent ); + if (variableDefinitionMatch) { + return buildGradleContent.replace( + variableDefinitionMatch[0], + `${variableDefinitionMatch[1]}${newVersion}${variableDefinitionMatch[3]}` + ); + } } } return null; @@ -214,8 +245,8 @@ function updateGlobalVariables( const match = regex.exec(buildGradleContent); if (match) { return buildGradleContent.replace( - variableDefinitionFormatMatch(variable), - `$1${newVersion}$3` + match[0], + `${match[1]}${newVersion}${match[3]}` ); } } @@ -237,8 +268,8 @@ function updateGlobalMapVariables( const match = regex.exec(buildGradleContent); if (match) { return buildGradleContent.replace( - variableMapDefinitionFormatMatch(variable, dependency.version), - `$1${newVersion}$3` + match[0], + `${match[1]}${newVersion}${match[3]}` ); } @@ -256,12 +287,15 @@ function updateKotlinVariablesByExtra( ): string | null { const variable = variables[`${dependency.group}:${dependency.name}`]; if (variable) { - const regex = new RegExp( + const regex = regEx( `(val ${variable} by extra(?: {|\\()\\s*")(.*)("\\s*[})])` ); const match = regex.exec(buildGradleContent); if (match) { - return buildGradleContent.replace(regex, `$1${newVersion}$3`); + return buildGradleContent.replace( + match[0], + `${match[1]}${newVersion}${match[3]}` + ); } } return null; @@ -274,10 +308,10 @@ function updatePropertyFileGlobalVariables( ): string | null { const variable = variables[`${dependency.group}:${dependency.name}`]; if (variable) { - const regex = new RegExp(`(${variable}\\s*=\\s*)(.*)`); + const regex = regEx(`(${variable}\\s*=\\s*)(.*)`); const match = regex.exec(buildGradleContent); if (match) { - return buildGradleContent.replace(regex, `$1${newVersion}`); + return buildGradleContent.replace(match[0], `${match[1]}${newVersion}`); } } return null;