From 67cbbba80b0ecea25c2f2f575b6cb0c1c72cff76 Mon Sep 17 00:00:00 2001 From: Rhys Arkins <rhys@arkins.net> Date: Mon, 4 Feb 2019 21:31:04 +0100 Subject: [PATCH] fix: Revert "feat(gradle): use maven datasource (#3062)" This reverts commit 0a2b212c76fe53925ae13dfa4d2162d563ce9622. --- lib/config/definitions.js | 1 - lib/manager/gradle/gradle-updates-report.js | 118 ------------------ lib/manager/gradle/index.js | 114 +++++++++++++---- .../gradle/MultiProjectUpdatesReport.json | 38 ------ test/_fixtures/gradle/updatesReport.json | 89 ++++++++++--- test/_fixtures/gradle/updatesReportEmpty.json | 32 +++-- test/datasource/maven.spec.js | 5 - .../gradle/__snapshots__/index.spec.js.snap | 113 ++++++++--------- test/manager/gradle/index.spec.js | 78 +++++++----- 9 files changed, 281 insertions(+), 307 deletions(-) delete mode 100644 lib/manager/gradle/gradle-updates-report.js delete mode 100644 test/_fixtures/gradle/MultiProjectUpdatesReport.json diff --git a/lib/config/definitions.js b/lib/config/definitions.js index 66abc9ae7e..24e074f838 100644 --- a/lib/config/definitions.js +++ b/lib/config/definitions.js @@ -1364,7 +1364,6 @@ const options = [ enabled: false, fileMatch: ['\\.gradle$', '(^|/)gradle.properties$'], timeout: 300, - versionScheme: 'maven', }, mergeable: true, cli: false, diff --git a/lib/manager/gradle/gradle-updates-report.js b/lib/manager/gradle/gradle-updates-report.js deleted file mode 100644 index bef15e6e7c..0000000000 --- a/lib/manager/gradle/gradle-updates-report.js +++ /dev/null @@ -1,118 +0,0 @@ -const path = require('path'); -const fs = require('fs-extra'); - -const GRADLE_DEPENDENCY_REPORT_FILENAME = 'gradle-renovate-report.json'; - -async function createRenovateGradlePlugin(localDir) { - const content = ` -import groovy.json.JsonOutput -import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency -import java.util.concurrent.ConcurrentLinkedQueue - -def output = new ConcurrentLinkedQueue<>(); - -allprojects { - tasks.register("renovate") { - doLast { - def project = ['project': project.name] - output << project - def repos = repositories - .collect { "$it.url" } - .findAll { !it.startsWith('file:') } - project.repositories = repos - def deps = configurations.collect { config -> - config.dependencies - .find { it instanceof DefaultExternalModuleDependency } - .collect { ['name':it.name, 'group':it.group, 'version':it.version] } - }.flatten() - project.dependencies = deps - } - } -} - -gradle.buildFinished { - def outputFile = new File('${GRADLE_DEPENDENCY_REPORT_FILENAME}') - def json = JsonOutput.toJson(output) - outputFile.write json -} `; - const gradleInitFile = path.join(localDir, 'renovate-plugin.gradle'); - logger.debug( - 'Creating renovate-plugin.gradle file with renovate gradle plugin' - ); - await fs.writeFile(gradleInitFile, content); -} - -async function extractDependenciesFromUpdatesReport(localDir) { - const gradleProjectConfigurations = await readGradleReport(localDir); - - const dependencies = gradleProjectConfigurations - .map(mergeDependenciesWithRepositories, []) - .reduce(flatternDependencies, []) - .reduce(combineReposOnDuplicatedDependencies, []); - - return dependencies.map(gradleModule => buildDependency(gradleModule)); -} - -async function readGradleReport(localDir) { - const renovateReportFilename = path.join( - localDir, - GRADLE_DEPENDENCY_REPORT_FILENAME - ); - if (!(await fs.exists(renovateReportFilename))) { - return []; - } - - const contents = await fs.readFile(renovateReportFilename, 'utf8'); - try { - return JSON.parse(contents); - } catch (e) { - logger.error('Invalid JSON', e); - return []; - } -} - -function mergeDependenciesWithRepositories(project) { - if (!project.dependencies) { - return []; - } - return project.dependencies.map(dep => ({ - ...dep, - repos: [...project.repositories], - })); -} - -function flatternDependencies(accumulator, currentValue) { - accumulator.push(...currentValue); - return accumulator; -} - -function combineReposOnDuplicatedDependencies(accumulator, currentValue) { - const existingDependency = accumulator.find( - dep => dep.name === currentValue.name - ); - if (!existingDependency) { - accumulator.push(currentValue); - } else { - existingDependency.repos.push(...currentValue.repos); - } - return accumulator; -} - -function buildDependency(gradleModule) { - const repositories = gradleModule.repos.join(','); - return { - name: gradleModule.name, - depGroup: gradleModule.group, - depName: `${gradleModule.group}:${gradleModule.name}`, - currentValue: gradleModule.version, - purl: `pkg:maven/${gradleModule.group}/${ - gradleModule.name - }?repository_url=${repositories}`, - }; -} - -module.exports = { - extractDependenciesFromUpdatesReport, - createRenovateGradlePlugin, - GRADLE_DEPENDENCY_REPORT_FILENAME, -}; diff --git a/lib/manager/gradle/index.js b/lib/manager/gradle/index.js index 174bfc5ab1..87b66b226a 100644 --- a/lib/manager/gradle/index.js +++ b/lib/manager/gradle/index.js @@ -3,10 +3,10 @@ const fs = require('fs-extra'); const path = require('path'); const gradle = require('./build-gradle'); -const updatesReport = require('./gradle-updates-report'); const GRADLE_DEPENDENCY_REPORT_COMMAND = - 'gradle --init-script renovate-plugin.gradle renovate'; + 'gradle --init-script init.gradle dependencyUpdates -Drevision=release'; +const GRADLE_DEPENDENCY_REPORT_FILENAME = 'build/dependencyUpdates/report.json'; const TIMEOUT_CODE = 143; async function extractAllPackageFiles(config, packageFiles) { @@ -25,56 +25,125 @@ async function extractAllPackageFiles(config, packageFiles) { } } - await updatesReport.createRenovateGradlePlugin(config.localDir); + await configureUseLatestVersionPlugin(config.localDir); const gradleSuccess = await executeGradle(config); if (!gradleSuccess) { - logger.warn('No gradle dependencies found'); return null; } gradle.init(); - - const dependencies = await updatesReport.extractDependenciesFromUpdatesReport( - config.localDir - ); - if (dependencies.length === 0) { - return []; - } - const gradleFiles = []; for (const packageFile of packageFiles) { const content = await platform.getFile(packageFile); if (content) { - gradleFiles.push({ - packageFile, - manager: 'gradle', - deps: dependencies, - }); - - gradle.collectVersionVariables(dependencies, content); + const deps = await extractPackageFile(content, packageFile, config); + if (deps) { + gradleFiles.push({ + packageFile, + manager: 'gradle', + ...deps, + }); + } } else { logger.info({ packageFile }, 'packageFile has no content'); } } - return gradleFiles; } +async function extractPackageFile(content, fileName, config) { + logger.debug(`gradle.extractPackageFile(filename=${fileName})`); + const gradleFile = path.join(config.localDir, fileName); + const baseDir = path.dirname(gradleFile); + + const deps = await extractDependenciesFromUpdatesReport(baseDir); + gradle.collectVersionVariables(deps, content); + return deps.length > 0 ? { deps } : null; +} + +function getPackageUpdates(config) { + logger.debug(`gradle.getPackageUpdates(depName=${config.depName})`); + + if (config.available) { + return [ + { + depName: config.depName, + newValue: config.available.release, + }, + ]; + } + return []; +} + function updateDependency(fileContent, upgrade) { // prettier-ignore logger.debug(`gradle.updateDependency(): packageFile:${upgrade.packageFile} depName:${upgrade.depName}, version:${upgrade.version} ==> ${upgrade.newValue}`); - return gradle.updateGradleVersion( + const newFileContent = gradle.updateGradleVersion( fileContent, buildGradleDependency(upgrade), - upgrade.newValue + upgrade.available.release ); + return newFileContent; +} + +async function configureUseLatestVersionPlugin(localDir) { + const content = ` +gradle.projectsLoaded { + rootProject.allprojects { + buildscript { + repositories { + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "gradle.plugin.se.patrikerdes:gradle-use-latest-versions-plugin:0.2.3" + classpath 'com.github.ben-manes:gradle-versions-plugin:0.17.0' + } + } + afterEvaluate { project -> + project.apply plugin: 'com.github.ben-manes.versions' + project.apply plugin: 'se.patrikerdes.use-latest-versions' + } + } +} + `; + const gradleInitFile = path.join(localDir, 'init.gradle'); + logger.debug('Creating init.gradle file'); + await fs.writeFile(gradleInitFile, content); } function buildGradleDependency(config) { return { group: config.depGroup, name: config.name, version: config.version }; } +async function extractDependenciesFromUpdatesReport(localDir) { + const filename = path.join(localDir, GRADLE_DEPENDENCY_REPORT_FILENAME); + if (!(await fs.exists(filename))) { + return []; + } + + const contents = await fs.readFile( + path.join(localDir, GRADLE_DEPENDENCY_REPORT_FILENAME), + 'utf8' + ); + const dependencies = JSON.parse(contents); + const combinedGradleDeps = dependencies.current.dependencies.concat( + dependencies.exceeded.dependencies, + dependencies.outdated.dependencies, + dependencies.unresolved.dependencies + ); + return combinedGradleDeps.map(gradleModule => { + const dependency = { ...gradleModule }; + delete dependency.group; + dependency.depGroup = gradleModule.group; + dependency.depName = `${gradleModule.group}:${gradleModule.name}`; + dependency.currentValue = gradleModule.version; + return dependency; + }); +} + async function executeGradle(config) { let stdout; let stderr; @@ -115,6 +184,7 @@ function getDockerRenovateGradleCommandLine(localDir) { module.exports = { extractAllPackageFiles, + getPackageUpdates, updateDependency, language: 'java', }; diff --git a/test/_fixtures/gradle/MultiProjectUpdatesReport.json b/test/_fixtures/gradle/MultiProjectUpdatesReport.json deleted file mode 100644 index 962e19376f..0000000000 --- a/test/_fixtures/gradle/MultiProjectUpdatesReport.json +++ /dev/null @@ -1,38 +0,0 @@ -[ - { - "project": "project1", - "repositories": [ - "https://repo.maven.apache.org/maven2/" - ], - "dependencies": [ - { - "name": "spring-boot-starter-jersey", - "group": "org.springframework.boot", - "version": null - }, - { - "name": "cglib-nodep", - "group": "cglib", - "version": "3.1" - } - ] - }, - { - "project": "project2", - "repositories": [ - "https://jitpack.io" - ], - "dependencies": [ - { - "name": "spring-boot-starter-jersey", - "group": "org.springframework.boot", - "version": null - }, - { - "name": "spock-core", - "group": "org.spockframework", - "version": "1.0-groovy-2.4" - } - ] - } -] diff --git a/test/_fixtures/gradle/updatesReport.json b/test/_fixtures/gradle/updatesReport.json index c11002de59..6e315ec129 100644 --- a/test/_fixtures/gradle/updatesReport.json +++ b/test/_fixtures/gradle/updatesReport.json @@ -1,26 +1,79 @@ -[ - { - "project": "telegram-images", - "repositories": [ - "https://repo.maven.apache.org/maven2/", - "https://jitpack.io" - ], +{ + "current": { "dependencies": [ { - "name": "spring-boot-starter-jersey", - "group": "org.springframework.boot", - "version": null + "group": "com.fkorotkov", + "version": "0.1", + "name": "gradle-libraries-plugin" }, { - "name": "spock-core", - "group": "org.spockframework", - "version": "1.0-groovy-2.4" + "group": "gradle.plugin.se.patrikerdes", + "version": "0.2.3", + "name": "gradle-use-latest-versions-plugin" }, { - "name": "cglib-nodep", + "group": "org.hamcrest", + "version": "1.3", + "name": "hamcrest-core" + } + ], + "count": 3 + }, + "exceeded": { + "dependencies": [ + ], + "count": 0 + }, + "outdated": { + "dependencies": [ + { "group": "cglib", - "version": "3.1" + "available": { + "release": "3.2.8", + "milestone": null, + "integration": null + }, + "version": "3.1", + "name": "cglib-nodep" + }, + { + "group": "org.grails", + "available": { + "release": "6.1.10.RELEASE", + "milestone": null, + "integration": null + }, + "version": "6.0.9.RELEASE", + "name": "gorm-hibernate5-spring-boot" + }, + { + "group": "mysql", + "available": { + "release": "8.0.12", + "milestone": null, + "integration": null + }, + "version": "5.1.41", + "name": "mysql-connector-java" + }, + { + "group": "org.springframework.boot", + "available": { + "release": "2.0.5.RELEASE", + "milestone": null, + "integration": null + }, + "version": "1.5.2.RELEASE", + "name": "spring-boot-starter-test" } - ] - } -] + ], + "count": 4 + }, + "unresolved": { + "dependencies": [ + + ], + "count": 0 + }, + "count": 7 +} diff --git a/test/_fixtures/gradle/updatesReportEmpty.json b/test/_fixtures/gradle/updatesReportEmpty.json index 7a6996ca94..2825737569 100644 --- a/test/_fixtures/gradle/updatesReportEmpty.json +++ b/test/_fixtures/gradle/updatesReportEmpty.json @@ -1,9 +1,23 @@ -[ - { - "project": "telegram-images", - "repositories": [ - "https://repo.maven.apache.org/maven2/", - "https://jitpack.io" - ] - } -] +{ + "current": { + "dependencies": [ + ], + "count": 0 + }, + "exceeded": { + "dependencies": [ + ], + "count": 0 + }, + "outdated": { + "dependencies": [ + ], + "count": 0 + }, + "unresolved": { + "dependencies": [ + ], + "count": 0 + }, + "count": 0 +} diff --git a/test/datasource/maven.spec.js b/test/datasource/maven.spec.js index 4fbac0bf3d..56b42d72a1 100644 --- a/test/datasource/maven.spec.js +++ b/test/datasource/maven.spec.js @@ -33,7 +33,6 @@ const config = { describe('datasource/maven', () => { beforeEach(() => { - nock.disableNetConnect(); nock('http://central.maven.org') .get('/maven2/mysql/mysql-connector-java/maven-metadata.xml') .reply(200, MYSQL_MAVEN_METADATA); @@ -50,10 +49,6 @@ describe('datasource/maven', () => { .reply(200, 'non-sense'); }); - afterEach(() => { - nock.enableNetConnect(); - }); - describe('getPkgReleases', () => { it('should return empty if library is not found', async () => { const releases = await datasource.getPkgReleases({ diff --git a/test/manager/gradle/__snapshots__/index.spec.js.snap b/test/manager/gradle/__snapshots__/index.spec.js.snap index 1c690178c7..53ed5f5d71 100644 --- a/test/manager/gradle/__snapshots__/index.spec.js.snap +++ b/test/manager/gradle/__snapshots__/index.spec.js.snap @@ -5,84 +5,73 @@ Array [ Object { "deps": Array [ Object { - "currentValue": null, - "depGroup": "org.springframework.boot", - "depName": "org.springframework.boot:spring-boot-starter-jersey", - "name": "spring-boot-starter-jersey", - "purl": "pkg:maven/org.springframework.boot/spring-boot-starter-jersey?repository_url=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", - "purl": "pkg:maven/org.spockframework/spock-core?repository_url=https://repo.maven.apache.org/maven2/,https://jitpack.io", - }, - Object { - "currentValue": "3.1", - "depGroup": "cglib", - "depName": "cglib:cglib-nodep", - "name": "cglib-nodep", - "purl": "pkg:maven/cglib/cglib-nodep?repository_url=https://repo.maven.apache.org/maven2/,https://jitpack.io", + "currentValue": "0.1", + "depGroup": "com.fkorotkov", + "depName": "com.fkorotkov:gradle-libraries-plugin", + "name": "gradle-libraries-plugin", + "version": "0.1", }, - ], - "manager": "gradle", - "packageFile": "build.gradle", - }, - Object { - "deps": Array [ Object { - "currentValue": null, - "depGroup": "org.springframework.boot", - "depName": "org.springframework.boot:spring-boot-starter-jersey", - "name": "spring-boot-starter-jersey", - "purl": "pkg:maven/org.springframework.boot/spring-boot-starter-jersey?repository_url=https://repo.maven.apache.org/maven2/,https://jitpack.io", + "currentValue": "0.2.3", + "depGroup": "gradle.plugin.se.patrikerdes", + "depName": "gradle.plugin.se.patrikerdes:gradle-use-latest-versions-plugin", + "name": "gradle-use-latest-versions-plugin", + "version": "0.2.3", }, Object { - "currentValue": "1.0-groovy-2.4", - "depGroup": "org.spockframework", - "depName": "org.spockframework:spock-core", - "name": "spock-core", - "purl": "pkg:maven/org.spockframework/spock-core?repository_url=https://repo.maven.apache.org/maven2/,https://jitpack.io", + "currentValue": "1.3", + "depGroup": "org.hamcrest", + "depName": "org.hamcrest:hamcrest-core", + "name": "hamcrest-core", + "version": "1.3", }, Object { + "available": Object { + "integration": null, + "milestone": null, + "release": "3.2.8", + }, "currentValue": "3.1", "depGroup": "cglib", "depName": "cglib:cglib-nodep", "name": "cglib-nodep", - "purl": "pkg:maven/cglib/cglib-nodep?repository_url=https://repo.maven.apache.org/maven2/,https://jitpack.io", + "version": "3.1", }, - ], - "manager": "gradle", - "packageFile": "subproject/build.gradle", - }, -] -`; - -exports[`manager/gradle extractPackageFile should use repositories only for current project 1`] = ` -Array [ - Object { - "deps": Array [ Object { - "currentValue": null, - "depGroup": "org.springframework.boot", - "depName": "org.springframework.boot:spring-boot-starter-jersey", - "name": "spring-boot-starter-jersey", - "purl": "pkg:maven/org.springframework.boot/spring-boot-starter-jersey?repository_url=https://repo.maven.apache.org/maven2/,https://jitpack.io", + "available": Object { + "integration": null, + "milestone": null, + "release": "6.1.10.RELEASE", + }, + "currentValue": "6.0.9.RELEASE", + "depGroup": "org.grails", + "depName": "org.grails:gorm-hibernate5-spring-boot", + "name": "gorm-hibernate5-spring-boot", + "version": "6.0.9.RELEASE", }, Object { - "currentValue": "3.1", - "depGroup": "cglib", - "depName": "cglib:cglib-nodep", - "name": "cglib-nodep", - "purl": "pkg:maven/cglib/cglib-nodep?repository_url=https://repo.maven.apache.org/maven2/", + "available": Object { + "integration": null, + "milestone": null, + "release": "8.0.12", + }, + "currentValue": "5.1.41", + "depGroup": "mysql", + "depName": "mysql:mysql-connector-java", + "name": "mysql-connector-java", + "version": "5.1.41", }, Object { - "currentValue": "1.0-groovy-2.4", - "depGroup": "org.spockframework", - "depName": "org.spockframework:spock-core", - "name": "spock-core", - "purl": "pkg:maven/org.spockframework/spock-core?repository_url=https://jitpack.io", + "available": Object { + "integration": null, + "milestone": null, + "release": "2.0.5.RELEASE", + }, + "currentValue": "1.5.2.RELEASE", + "depGroup": "org.springframework.boot", + "depName": "org.springframework.boot:spring-boot-starter-test", + "name": "spring-boot-starter-test", + "version": "1.5.2.RELEASE", }, ], "manager": "gradle", diff --git a/test/manager/gradle/index.spec.js b/test/manager/gradle/index.spec.js index 3f3fe529a3..4351bdc866 100644 --- a/test/manager/gradle/index.spec.js +++ b/test/manager/gradle/index.spec.js @@ -5,9 +5,6 @@ const { toUnix } = require('upath'); const fs = require('fs-extra'); const fsReal = require('fs'); const { exec } = require('child-process-promise'); -const { initLogger } = require('../../../lib/logger'); - -initLogger(); const manager = require('../../../lib/manager/gradle/index'); @@ -37,7 +34,6 @@ describe('manager/gradle', () => { it('should return gradle dependencies', async () => { const dependencies = await manager.extractAllPackageFiles(config, [ 'build.gradle', - 'subproject/build.gradle', ]); expect(dependencies).toMatchSnapshot(); }); @@ -76,36 +72,11 @@ describe('manager/gradle', () => { expect(dependencies).toEqual([]); }); - it('should return empty if renovate report is invalid', async () => { - const renovateReport = ` - Invalid JSON] - `; - fs.readFile.mockReturnValue(renovateReport); - - const dependencies = await manager.extractAllPackageFiles(config, [ - 'build.gradle', - ]); - expect(dependencies).toEqual([]); - }); - - it('should use repositories only for current project', async () => { - const multiProjectUpdatesReport = fsReal.readFileSync( - 'test/_fixtures/gradle/MultiProjectUpdatesReport.json', - 'utf8' - ); - fs.readFile.mockReturnValue(multiProjectUpdatesReport); - - const dependencies = await manager.extractAllPackageFiles(config, [ - 'build.gradle', - ]); - expect(dependencies).toMatchSnapshot(); - }); - it('should execute gradle with the proper parameters', async () => { await manager.extractAllPackageFiles(config, ['build.gradle']); expect(exec.mock.calls[0][0]).toBe( - 'gradle --init-script renovate-plugin.gradle renovate' + 'gradle --init-script init.gradle dependencyUpdates -Drevision=release' ); expect(exec.mock.calls[0][1]).toMatchObject({ cwd: 'localDir', @@ -122,6 +93,14 @@ describe('manager/gradle', () => { expect(exec.mock.calls.length).toBe(0); }); + it('should return empty if not content', async () => { + platform.getFile.mockReturnValue(null); + const res = await manager.extractAllPackageFiles(config, [ + 'build.gradle', + ]); + expect(res).toEqual([]); + }); + it('should write files before extracting', async () => { const packageFiles = ['build.gradle', 'foo/build.gradle']; await manager.extractAllPackageFiles(config, packageFiles); @@ -146,11 +125,11 @@ describe('manager/gradle', () => { expect(fs.outputFile.mock.calls.length).toBe(0); }); - it('should configure the renovate report plugin', async () => { + it('should configure the useLatestVersion plugin', async () => { await manager.extractAllPackageFiles(config, ['build.gradle']); expect(toUnix(fs.writeFile.mock.calls[0][0])).toBe( - 'localDir/renovate-plugin.gradle' + 'localDir/init.gradle' ); }); @@ -162,11 +141,41 @@ describe('manager/gradle', () => { await manager.extractAllPackageFiles(configWithDocker, ['build.gradle']); expect(exec.mock.calls[0][0]).toBe( - 'docker run --rm -v localDir:localDir -w localDir renovate/gradle gradle --init-script renovate-plugin.gradle renovate' + 'docker run --rm -v localDir:localDir -w localDir renovate/gradle gradle --init-script init.gradle dependencyUpdates -Drevision=release' ); }); }); + describe('getPackageUpdates', () => { + it('should return the new version if it is available', async () => { + const newVersion = { + ...config, + depName: 'cglib:cglib-nodep', + available: { + release: '3.2.8', + }, + }; + const outdatedDependencies = await manager.getPackageUpdates(newVersion); + + expect(outdatedDependencies).toMatchObject([ + { + depName: 'cglib:cglib-nodep', + newValue: '3.2.8', + }, + ]); + }); + + it('should return empty if there is no new version', async () => { + const newVersion = { + ...config, + depName: 'cglib:cglib-nodep', + }; + const outdatedDependencies = await manager.getPackageUpdates(newVersion); + + expect(outdatedDependencies).toMatchObject([]); + }); + }); + describe('updateDependency', () => { it('should update an existing dependency', () => { const buildGradleContent = fsReal.readFileSync( @@ -175,7 +184,8 @@ describe('manager/gradle', () => { ); // prettier-ignore const upgrade = { - depGroup: 'cglib', name: 'cglib-nodep', version: '3.1', newValue: '3.2.8' + depGroup: 'cglib', name: 'cglib-nodep', version: '3.1', + available: { release: '3.2.8', milestone: null, integration: null }, }; const buildGradleContentUpdated = manager.updateDependency( buildGradleContent, -- GitLab