From 3a09b46b5a96b85e7874f9d34b159b7c3045e11d Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Sun, 14 Jul 2019 18:01:36 +0900 Subject: [PATCH 1/2] feat(gradle): Add basic Gradle Kotlin DSL support --- lib/config/definitions.js | 6 +- lib/manager/gradle/build-gradle.ts | 36 ++++++++- lib/manager/gradle/index.ts | 8 +- renovate-schema.json | 6 +- .../gradle/__snapshots__/index.spec.ts.snap | 79 +++++++++++++++++++ test/manager/gradle/build-gradle.spec.ts | 58 ++++++++++++++ test/manager/gradle/index.spec.ts | 37 +++++++++ website/docs/java.md | 4 +- 8 files changed, 226 insertions(+), 8 deletions(-) diff --git a/lib/config/definitions.js b/lib/config/definitions.js index e524139c62c474..c6e01366b4e233 100644 --- a/lib/config/definitions.js +++ b/lib/config/definitions.js @@ -1644,7 +1644,11 @@ const options = [ stage: 'package', type: 'object', default: { - fileMatch: ['\\.gradle$', '(^|/)gradle.properties$'], + fileMatch: [ + '\\.gradle(\\.kts)?$', + '(^|/)gradle.properties$', + '(^|/)buildSrc/.*', + ], timeout: 300, versionScheme: 'maven', }, diff --git a/lib/manager/gradle/build-gradle.ts b/lib/manager/gradle/build-gradle.ts index 4174fa7b9dd11c..4322fbe79356a5 100644 --- a/lib/manager/gradle/build-gradle.ts +++ b/lib/manager/gradle/build-gradle.ts @@ -64,6 +64,7 @@ export function collectVersionVariables( moduleStringVariableExpressionVersionFormatMatch(dependency), moduleStringVariableInterpolationVersionFormatMatch(dependency), moduleMapVariableVersionFormatMatch(dependency), + moduleKotlinNamedArgumentVariableVersionFormatMatch(dependency), ]; for (const regex of regexes) { @@ -86,8 +87,10 @@ function updateVersionLiterals( ) { const regexes: RegExp[] = [ moduleStringVersionFormatMatch(dependency), - pluginStringVersionFormatMatch(dependency), + groovyPluginStringVersionFormatMatch(dependency), + kotlinPluginStringVersionFormatMatch(dependency), moduleMapVersionFormatMatch(dependency), + moduleKotlinNamedArgumentVersionFormatMatch(dependency), ]; for (const regex of regexes) { if (buildGradleContent.match(regex)) { @@ -106,6 +109,7 @@ function updateLocalVariables( moduleMapVariableVersionFormatMatch(dependency), moduleStringVariableInterpolationVersionFormatMatch(dependency), moduleStringVariableExpressionVersionFormatMatch(dependency), + moduleKotlinNamedArgumentVariableVersionFormatMatch(dependency), ]; for (const regex of regexes) { const match = buildGradleContent.match(regex); @@ -161,12 +165,18 @@ function moduleStringVersionFormatMatch(dependency: GradleDependency) { ); } -function pluginStringVersionFormatMatch(dependency: GradleDependency) { +function groovyPluginStringVersionFormatMatch(dependency: GradleDependency) { return new RegExp( `(id\\s+["']${dependency.group}["']\\s+version\\s+["'])[^$].*?(["'])` ); } +function kotlinPluginStringVersionFormatMatch(dependency: GradleDependency) { + return new RegExp( + `(id\\("${dependency.group}"\\)\\s+version\\s+")[^$].*?(")` + ); +} + function moduleMapVersionFormatMatch(dependency: GradleDependency) { // prettier-ignore return new RegExp( @@ -176,6 +186,17 @@ function moduleMapVersionFormatMatch(dependency: GradleDependency) { ); } +function moduleKotlinNamedArgumentVersionFormatMatch( + dependency: GradleDependency +) { + // prettier-ignore + return new RegExp( + `(group\\s*=\\s*"${dependency.group}"\\s*,\\s*` + + `name\\s*=\\s*"${dependency.name}"\\s*,\\s*` + + `version\\s*=\\s*").*?(")` + ); +} + function moduleMapVariableVersionFormatMatch(dependency: GradleDependency) { // prettier-ignore return new RegExp( @@ -185,6 +206,17 @@ function moduleMapVariableVersionFormatMatch(dependency: GradleDependency) { ); } +function moduleKotlinNamedArgumentVariableVersionFormatMatch( + dependency: GradleDependency +) { + // prettier-ignore + return new RegExp( + `group\\s*=\\s*"${dependency.group}"\\s*,\\s*` + + `name\\s*=\\s*"${dependency.name}"\\s*,\\s*` + + `version\\s*=\\s*([^\\s"]+?)[\\s\\),]` + ); +} + function moduleStringVariableInterpolationVersionFormatMatch( dependency: GradleDependency ) { diff --git a/lib/manager/gradle/index.ts b/lib/manager/gradle/index.ts index 353255b4bce040..fd3a572de5d29d 100644 --- a/lib/manager/gradle/index.ts +++ b/lib/manager/gradle/index.ts @@ -22,8 +22,12 @@ export async function extractAllPackageFiles( config: ExtractConfig, packageFiles: string[] ): Promise { - if (!packageFiles.some(packageFile => packageFile === 'build.gradle')) { - logger.warn('No root build.gradle found - skipping'); + if ( + !packageFiles.some(packageFile => + ['build.gradle', 'build.gradle.kts'].includes(packageFile) + ) + ) { + logger.warn('No root build.gradle nor build.gradle.kts found - skipping'); return null; } logger.info('Extracting dependencies from all gradle files'); diff --git a/renovate-schema.json b/renovate-schema.json index 09c4387cc8baa4..431f9d181167ca 100644 --- a/renovate-schema.json +++ b/renovate-schema.json @@ -1130,7 +1130,11 @@ "description": "Configuration object for build.gradle files", "type": "object", "default": { - "fileMatch": ["\\.gradle$", "(^|/)gradle.properties$"], + "fileMatch": [ + "\\.gradle(\\.kts)?$", + "(^|/)gradle.properties$", + "(^|/)buildSrc/.*" + ], "timeout": 300, "versionScheme": "maven" }, diff --git a/test/manager/gradle/__snapshots__/index.spec.ts.snap b/test/manager/gradle/__snapshots__/index.spec.ts.snap index be187adcdc2218..c3751811704d9e 100644 --- a/test/manager/gradle/__snapshots__/index.spec.ts.snap +++ b/test/manager/gradle/__snapshots__/index.spec.ts.snap @@ -79,6 +79,85 @@ Array [ ] `; +exports[`manager/gradle extractPackageFile should return gradle.kts dependencies 1`] = ` +Array [ + Object { + "datasource": "maven", + "deps": Array [ + Object { + "currentValue": null, + "depGroup": "org.springframework.boot", + "depName": "org.springframework.boot:spring-boot-starter-jersey", + "name": "spring-boot-starter-jersey", + "registryUrls": Array [ + "https://repo.maven.apache.org/maven2/", + "https://jitpack.io", + ], + }, + Object { + "currentValue": "1.0-groovy-2.4", + "depGroup": "org.spockframework", + "depName": "org.spockframework:spock-core", + "name": "spock-core", + "registryUrls": Array [ + "https://repo.maven.apache.org/maven2/", + "https://jitpack.io", + ], + }, + Object { + "currentValue": "3.1", + "depGroup": "cglib", + "depName": "cglib:cglib-nodep", + "name": "cglib-nodep", + "registryUrls": Array [ + "https://repo.maven.apache.org/maven2/", + "https://jitpack.io", + ], + }, + ], + "manager": "gradle", + "packageFile": "build.gradle.kts", + }, + Object { + "datasource": "maven", + "deps": Array [ + Object { + "currentValue": null, + "depGroup": "org.springframework.boot", + "depName": "org.springframework.boot:spring-boot-starter-jersey", + "name": "spring-boot-starter-jersey", + "registryUrls": Array [ + "https://repo.maven.apache.org/maven2/", + "https://jitpack.io", + ], + }, + Object { + "currentValue": "1.0-groovy-2.4", + "depGroup": "org.spockframework", + "depName": "org.spockframework:spock-core", + "name": "spock-core", + "registryUrls": Array [ + "https://repo.maven.apache.org/maven2/", + "https://jitpack.io", + ], + }, + Object { + "currentValue": "3.1", + "depGroup": "cglib", + "depName": "cglib:cglib-nodep", + "name": "cglib-nodep", + "registryUrls": Array [ + "https://repo.maven.apache.org/maven2/", + "https://jitpack.io", + ], + }, + ], + "manager": "gradle", + "packageFile": "subproject/build.gradle.kts", + }, +] +`; + exports[`manager/gradle extractPackageFile should throw registry failure if gradle execution fails 1`] = `[Error: registry-failure]`; exports[`manager/gradle extractPackageFile should use repositories only for current project 1`] = ` diff --git a/test/manager/gradle/build-gradle.spec.ts b/test/manager/gradle/build-gradle.spec.ts index da7d7c86cfe40a..ae058cde606ffb 100644 --- a/test/manager/gradle/build-gradle.spec.ts +++ b/test/manager/gradle/build-gradle.spec.ts @@ -82,6 +82,22 @@ describe('lib/manager/gradle/updateGradleVersion', () => { ); }); + it('returns a file updated if the version defined as a Kotlin named argument is found', () => { + const gradleFile = `compile(group = "mysql" , + name = "mysql-connector-java", + version = "6.0.5")`; + const updatedGradleFile = updateGradleVersion( + gradleFile, + { group: 'mysql', name: 'mysql-connector-java', version: '6.0.5' }, + '7.0.0' + ); + expect(updatedGradleFile).toEqual( + `compile(group = "mysql" , + name = "mysql-connector-java", + version = "7.0.0")` + ); + }); + it('should returns a file updated if the version defined in a variable as a string is found', () => { const gradleFile = `String mysqlVersion= "6.0.5" runtime ( "mysql:mysql-connector-java:$mysqlVersion" ) @@ -130,6 +146,26 @@ describe('lib/manager/gradle/updateGradleVersion', () => { ); }); + it('should returns a file updated if the version defined in a variable as a Kotlin named argument is found', () => { + const gradleFile = `val mysqlVersion = "6.0.5" + compile(group = "mysql" , + name = "mysql-connector-java", + 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" + compile(group = "mysql" , + name = "mysql-connector-java", + 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 @@ -192,6 +228,28 @@ describe('lib/manager/gradle/updateGradleVersion', () => { expect(updatedGradleFile).toEqual('String mysqlVersion = "7.0.0"'); }); + it('should replace a external variable assigned to a Kotlin named argument dependency', () => { + const gradleFile = `compile(group = "mysql" , + name = "mysql-connector-java", + version = mysqlVersion) + `; + const mysqlDependency = { + group: 'mysql', + depGroup: 'mysql', + name: 'mysql-connector-java', + version: '6.0.5', + }; + collectVersionVariables([mysqlDependency], gradleFile); + + const gradleWithVersionFile = 'val mysqlVersion = "6.0.5"'; + const updatedGradleFile = updateGradleVersion( + gradleWithVersionFile, + mysqlDependency, + '7.0.0' + ); + expect(updatedGradleFile).toEqual('val mysqlVersion = "7.0.0"'); + }); + it('should replace a external variable assigned to a interpolated dependency', () => { const gradleFile = 'runtime ( "mysql:mysql-connector-java:$mysqlVersion" )'; diff --git a/test/manager/gradle/index.spec.ts b/test/manager/gradle/index.spec.ts index 81a2ca3fe3f8f8..88b3074d996ed8 100644 --- a/test/manager/gradle/index.spec.ts +++ b/test/manager/gradle/index.spec.ts @@ -42,6 +42,14 @@ describe('manager/gradle', () => { expect(dependencies).toMatchSnapshot(); }); + it('should return gradle.kts dependencies', async () => { + const dependencies = await manager.extractAllPackageFiles(config, [ + 'build.gradle.kts', + 'subproject/build.gradle.kts', + ]); + expect(dependencies).toMatchSnapshot(); + }); + it('should return empty if there are no dependencies', async () => { fs.readFile.mockResolvedValue(fsReal.readFileSync( 'test/datasource/gradle/_fixtures/updatesReportEmpty.json', @@ -201,5 +209,34 @@ describe('manager/gradle', () => { 'id "com.github.ben-manes.versions" version "0.20.0"' ); }); + + it('should update an existing plugin dependency with Kotlin DSL', () => { + const buildGradleContent = ` + plugins { + id("com.github.ben-manes.versions") version "0.20.0" + } + `; + const upgrade = { + depGroup: 'com.github.ben-manes.versions', + name: 'com.github.ben-manes.versions.gradle.plugin', + version: '0.20.0', + newValue: '0.21.0', + }; + const buildGradleContentUpdated = manager.updateDependency( + buildGradleContent, + upgrade + ); + + expect(buildGradleContent).not.toMatch( + 'id("com.github.ben-manes.versions") version "0.21.0"' + ); + + expect(buildGradleContentUpdated).toMatch( + 'id("com.github.ben-manes.versions") version "0.21.0"' + ); + expect(buildGradleContentUpdated).not.toMatch( + 'id("com.github.ben-manes.versions") version "0.20.0"' + ); + }); }); }); diff --git a/website/docs/java.md b/website/docs/java.md index 36618ad8dad92f..87be83fdca4f05 100644 --- a/website/docs/java.md +++ b/website/docs/java.md @@ -13,11 +13,11 @@ Renovate detects versions specified as string `'group:artifact:version'` and as ### File Support -Renovate can update `build.gradle` files in the root of the repository and any `*.gradle` file inside any subdirectory as multi-project configurations. +Renovate can update `build.gradle`/`build.gradle.kts` files in the root of the repository and any `*.gradle`/`*.gradle.kts` file inside any subdirectory as multi-project configurations. Renovate does not support: -- Projects without a `build.gradle` file in the root of the repository. +- Projects without neither `build.gradle` nor `build.gradle.kts` file in the root of the repository. - Android projects that requires extra configuration to run. (e.g. setting the android SDK) ### How It Works From 55389ff0d9149906a1260827f2f8231ae904a907 Mon Sep 17 00:00:00 2001 From: IKEDA Sho Date: Tue, 30 Jul 2019 02:37:26 +0900 Subject: [PATCH 2/2] Omit buildSrc from the fileMatch --- lib/config/definitions.js | 6 +----- renovate-schema.json | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/config/definitions.js b/lib/config/definitions.js index c6e01366b4e233..e6ff3a1819a846 100644 --- a/lib/config/definitions.js +++ b/lib/config/definitions.js @@ -1644,11 +1644,7 @@ const options = [ stage: 'package', type: 'object', default: { - fileMatch: [ - '\\.gradle(\\.kts)?$', - '(^|/)gradle.properties$', - '(^|/)buildSrc/.*', - ], + fileMatch: ['\\.gradle(\\.kts)?$', '(^|/)gradle.properties$'], timeout: 300, versionScheme: 'maven', }, diff --git a/renovate-schema.json b/renovate-schema.json index 431f9d181167ca..7559eb8d617fa1 100644 --- a/renovate-schema.json +++ b/renovate-schema.json @@ -1130,11 +1130,7 @@ "description": "Configuration object for build.gradle files", "type": "object", "default": { - "fileMatch": [ - "\\.gradle(\\.kts)?$", - "(^|/)gradle.properties$", - "(^|/)buildSrc/.*" - ], + "fileMatch": ["\\.gradle(\\.kts)?$", "(^|/)gradle.properties$"], "timeout": 300, "versionScheme": "maven" },