From d9349b21b97b9d770c116efac25e57b851f99e84 Mon Sep 17 00:00:00 2001 From: Johannes Feichtner <Churro@users.noreply.github.com> Date: Tue, 27 Dec 2022 14:45:30 +0100 Subject: [PATCH] refactor(manager/gradle): group extraction tests + code hygiene (#19586) --- lib/modules/manager/gradle/extract.spec.ts | 1595 ++++++++--------- lib/modules/manager/gradle/extract.ts | 55 +- lib/modules/manager/gradle/parser.ts | 4 +- .../manager/gradle/parser/common.spec.ts | 2 +- lib/modules/manager/gradle/parser/handlers.ts | 6 +- lib/modules/manager/gradle/types.ts | 2 +- 6 files changed, 802 insertions(+), 862 deletions(-) diff --git a/lib/modules/manager/gradle/extract.spec.ts b/lib/modules/manager/gradle/extract.spec.ts index 7f09d81329..249c959102 100644 --- a/lib/modules/manager/gradle/extract.spec.ts +++ b/lib/modules/manager/gradle/extract.spec.ts @@ -1,4 +1,4 @@ -import { stripIndent } from 'common-tags'; +import { codeBlock, stripIndent } from 'common-tags'; import { Fixtures } from '../../../../test/fixtures'; import { fs, logger } from '../../../../test/util'; import type { ExtractConfig } from '../types'; @@ -35,17 +35,15 @@ describe('modules/manager/gradle/extract', () => { }); it('returns null', async () => { - mockFs({ + const fsMock = { 'gradle.properties': '', 'build.gradle': '', - }); - - const res = await extractAllPackageFiles({} as ExtractConfig, [ - 'build.gradle', - 'gradle.properties', - ]); + }; + mockFs(fsMock); - expect(res).toBeNull(); + expect( + await extractAllPackageFiles({} as ExtractConfig, Object.keys(fsMock)) + ).toBeNull(); }); it('logs a warning in case parseGradle throws an exception', async () => { @@ -63,35 +61,17 @@ describe('modules/manager/gradle/extract', () => { ); }); - it('extracts from cross-referenced files', async () => { - mockFs({ - 'gradle.properties': 'baz=1.2.3', - 'build.gradle': 'url "https://example.com"; "foo:bar:$baz"', - }); - - const res = await extractAllPackageFiles({} as ExtractConfig, [ - 'build.gradle', - 'gradle.properties', - ]); - - expect(res).toMatchObject([ - { - packageFile: 'gradle.properties', - deps: [{ depName: 'foo:bar', currentValue: '1.2.3' }], - }, - { packageFile: 'build.gradle', deps: [] }, - ]); - }); - it('skips versions composed from multiple variables', async () => { - mockFs({ + const fsMock = { 'build.gradle': 'foo = "1"; bar = "2"; baz = "3"; "foo:bar:$foo.$bar.$baz"', - }); + }; + mockFs(fsMock); - const res = await extractAllPackageFiles({} as ExtractConfig, [ - 'build.gradle', - ]); + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); expect(res).toMatchObject([ { @@ -100,30 +80,25 @@ describe('modules/manager/gradle/extract', () => { { depName: 'foo:bar', currentValue: '1.2.3', - registryUrls: [], skipReason: 'contains-variable', - managerData: { - packageFile: 'build.gradle', - }, }, ], }, ]); }); - it('works with file-ext-var', async () => { - mockFs({ + it('extracts from cross-referenced files', async () => { + const fsMock = { 'gradle.properties': 'baz=1.2.3', 'build.gradle': 'repositories { maven { url "https://example.com" } }; "foo:bar:$baz@zip"', - 'settings.gradle': null as never, // TODO: #7154 - }); + }; + mockFs(fsMock); - const res = await extractAllPackageFiles({} as ExtractConfig, [ - 'build.gradle', - 'gradle.properties', - 'settings.gradle', - ]); + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); expect(res).toMatchObject([ { @@ -136,11 +111,6 @@ describe('modules/manager/gradle/extract', () => { }, ], }, - { - datasource: 'maven', - deps: [], - packageFile: 'settings.gradle', - }, { packageFile: 'build.gradle', deps: [] }, ]); }); @@ -151,11 +121,8 @@ describe('modules/manager/gradle/extract', () => { 'build.gradle': 'foo = "1.0.1"', 'aaa/gradle.properties': 'bar = "2.0.0"', 'aaa/build.gradle': 'bar = "2.0.1"', - 'aaa/bbb/build.gradle': ['foo:foo:$foo', 'bar:bar:$bar'] - .map((x) => `"${x}"`) - .join('\n'), + 'aaa/bbb/build.gradle': '"foo:foo:$foo"; "bar:bar:$bar"', }; - mockFs(fsMock); const res = await extractAllPackageFiles( @@ -178,446 +145,516 @@ describe('modules/manager/gradle/extract', () => { ]); }); - it('deduplicates registry urls', async () => { - const fsMock = { - 'build.gradle': [ - 'repositories { maven { url "https://repo.maven.apache.org/maven2" } }', - 'repositories { maven { url "https://repo.maven.apache.org/maven2" } }', - 'repositories { maven { url "https://example.com" } }', - 'repositories { maven { url "https://example.com" } }', - 'id "foo.bar" version "1.2.3"', - '"foo:bar:1.2.3"', - ].join(';\n'), - }; - - mockFs(fsMock); - - const res = await extractAllPackageFiles( - {} as ExtractConfig, - Object.keys(fsMock) - ); - - expect(res).toMatchObject([ - { - packageFile: 'build.gradle', - deps: [ - { - depType: 'plugin', - registryUrls: [ - 'https://repo.maven.apache.org/maven2', - 'https://example.com', - 'https://plugins.gradle.org/m2/', - ], - }, - { - registryUrls: [ - 'https://repo.maven.apache.org/maven2', - 'https://example.com', - ], - }, - ], - }, - ]); - }); + it('filters duplicate dependency findings', async () => { + const buildFile = codeBlock` + apply from: 'test.gradle' - it('interpolates repository URLs', async () => { - const buildFile = ` repositories { - mavenCentral() - maven { - url = "\${repositoryBaseURL}/repository-build" - } - maven { - name = "baz" - url = "\${repositoryBaseURL}/\${name}" - } + mavenCentral() } dependencies { - implementation "com.google.protobuf:protobuf-java:2.17.0" + implementation "io.jsonwebtoken:jjwt-api:$\{jjwtVersion}" + runtimeOnly "io.jsonwebtoken:jjwt-impl:$\{jjwtVersion}" } `; - mockFs({ - 'build.gradle': buildFile, - 'gradle.properties': 'repositoryBaseURL: https\\://dummy.org/whatever', - }); - - const res = await extractAllPackageFiles({} as ExtractConfig, [ - 'build.gradle', - 'gradle.properties', - ]); - - expect(res).toMatchObject([ - { - packageFile: 'gradle.properties', - datasource: 'maven', - deps: [], - }, - { - packageFile: 'build.gradle', - datasource: 'maven', - deps: [ - { - depName: 'com.google.protobuf:protobuf-java', - currentValue: '2.17.0', - managerData: { - fileReplacePosition: 335, - packageFile: 'build.gradle', - }, - fileReplacePosition: 335, - registryUrls: [ - 'https://repo.maven.apache.org/maven2', - 'https://dummy.org/whatever/repository-build', - 'https://dummy.org/whatever/baz', - ], - }, - ], - }, - ]); - }); + const testFile = codeBlock` + ext.jjwtVersion = '0.11.2' - it('works with dependency catalogs', async () => { - const tomlFile = Fixtures.get('1/libs.versions.toml'); + ext { + jjwtApi = "io.jsonwebtoken:jjwt-api:$jjwtVersion" + } + `; const fsMock = { - 'gradle/libs.versions.toml': tomlFile, + 'build.gradle': buildFile, + 'test.gradle': testFile, }; mockFs(fsMock); + const res = await extractAllPackageFiles( {} as ExtractConfig, Object.keys(fsMock) ); + expect(res).toMatchObject([ { - packageFile: 'gradle/libs.versions.toml', + packageFile: 'test.gradle', deps: [ - { - depName: 'io.gitlab.arturbosch.detekt:detekt-formatting', - groupName: 'detekt', - currentValue: '1.17.0', - managerData: { - fileReplacePosition: 21, - packageFile: 'gradle/libs.versions.toml', - }, - }, - { - depName: 'io.kotest:kotest-assertions-core-jvm', - groupName: 'kotest', - currentValue: '4.6.0', - managerData: { - fileReplacePosition: 51, - packageFile: 'gradle/libs.versions.toml', - }, - }, - { - depName: 'io.kotest:kotest-runner-junit5', - groupName: 'kotest', - currentValue: '4.6.0', - managerData: { - fileReplacePosition: 51, - packageFile: 'gradle/libs.versions.toml', - }, - }, - { - depName: 'org.mockito:mockito-core', - groupName: 'org.mockito', - currentValue: '3.10.0', - managerData: { - fileReplacePosition: 474, - packageFile: 'gradle/libs.versions.toml', - }, - }, - { - depName: 'com.github.siom79.japicmp:japicmp', - groupName: 'com.github.siom79.japicmp', - currentValue: '0.15.+', - managerData: { - fileReplacePosition: 561, - packageFile: 'gradle/libs.versions.toml', - }, - }, - { - depName: 'guava', - skipReason: 'multiple-constraint-dep', - managerData: { - packageFile: 'gradle/libs.versions.toml', - }, - }, - { - depName: 'gson', - skipReason: 'unsupported-version', - managerData: { - packageFile: 'gradle/libs.versions.toml', - }, - }, - { - depName: 'io.gitlab.arturbosch.detekt', - depType: 'plugin', - currentValue: '1.17.0', - packageName: - 'io.gitlab.arturbosch.detekt:io.gitlab.arturbosch.detekt.gradle.plugin', - managerData: { - fileReplacePosition: 21, - packageFile: 'gradle/libs.versions.toml', - }, - registryUrls: ['https://plugins.gradle.org/m2/'], - }, - { - depName: 'org.danilopianini.publish-on-central', - depType: 'plugin', - currentValue: '0.5.0', - packageName: - 'org.danilopianini.publish-on-central:org.danilopianini.publish-on-central.gradle.plugin', - managerData: { - fileReplacePosition: 82, - packageFile: 'gradle/libs.versions.toml', - }, - registryUrls: ['https://plugins.gradle.org/m2/'], - }, - { - depName: 'org.ajoberstar.grgit', - depType: 'plugin', - commitMessageTopic: 'plugin grgit', - packageName: - 'org.ajoberstar.grgit:org.ajoberstar.grgit.gradle.plugin', - managerData: { - packageFile: 'gradle/libs.versions.toml', - }, - registryUrls: ['https://plugins.gradle.org/m2/'], - skipReason: 'unknown-version', - }, + { depName: 'io.jsonwebtoken:jjwt-api' }, + { depName: 'io.jsonwebtoken:jjwt-impl' }, ], }, + { packageFile: 'build.gradle' }, ]); }); - it("can run Javier's example", async () => { - const tomlFile = Fixtures.get('2/libs.versions.toml'); + it('ensures depType is assigned', async () => { const fsMock = { - 'gradle/libs.versions.toml': tomlFile, + 'build.gradle': + "id 'org.sonarqube' version '3.1.1'\n\"io.jsonwebtoken:jjwt-api:0.11.2\"", + 'buildSrc/build.gradle': '"com.google.protobuf:protobuf-java:3.18.2"', }; mockFs(fsMock); + const res = await extractAllPackageFiles( {} as ExtractConfig, Object.keys(fsMock) ); + expect(res).toMatchObject([ { - packageFile: 'gradle/libs.versions.toml', + packageFile: 'build.gradle', deps: [ - { - depName: 'com.squareup.okhttp3:okhttp', - groupName: 'com.squareup.okhttp3', - currentValue: '4.9.0', - managerData: { - fileReplacePosition: 99, - packageFile: 'gradle/libs.versions.toml', - }, - }, - { - depName: 'com.squareup.okio:okio', - groupName: 'com.squareup.okio', - currentValue: '2.8.0', - managerData: { - fileReplacePosition: 161, - packageFile: 'gradle/libs.versions.toml', - }, - }, - { - depName: 'com.squareup.picasso:picasso', - groupName: 'com.squareup.picasso', - currentValue: '2.5.1', - managerData: { - fileReplacePosition: 243, - packageFile: 'gradle/libs.versions.toml', - }, - }, - { - depName: 'com.squareup.retrofit2:retrofit', - groupName: 'retrofit', - currentValue: '2.8.2', - managerData: { - fileReplacePosition: 41, - packageFile: 'gradle/libs.versions.toml', - }, - }, - { - depName: 'google-firebase-analytics', - managerData: { - packageFile: 'gradle/libs.versions.toml', - }, - skipReason: 'no-version', - }, - { - depName: 'google-firebase-crashlytics', - managerData: { - packageFile: 'gradle/libs.versions.toml', - }, - skipReason: 'no-version', - }, - { - depName: 'google-firebase-messaging', - managerData: { - packageFile: 'gradle/libs.versions.toml', - }, - skipReason: 'no-version', - }, - { - depName: 'org.jetbrains.kotlin.jvm', - depType: 'plugin', - currentValue: '1.5.21', - commitMessageTopic: 'plugin kotlinJvm', - packageName: - 'org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin', - managerData: { - fileReplacePosition: 661, - packageFile: 'gradle/libs.versions.toml', - }, - registryUrls: ['https://plugins.gradle.org/m2/'], - }, - { - depName: 'org.jetbrains.kotlin.plugin.serialization', - depType: 'plugin', - currentValue: '1.5.21', - packageName: - 'org.jetbrains.kotlin.plugin.serialization:org.jetbrains.kotlin.plugin.serialization.gradle.plugin', - managerData: { - fileReplacePosition: 21, - packageFile: 'gradle/libs.versions.toml', - }, - registryUrls: ['https://plugins.gradle.org/m2/'], - }, - { - depName: 'org.danilopianini.multi-jvm-test-plugin', - depType: 'plugin', - currentValue: '0.3.0', - commitMessageTopic: 'plugin multiJvm', - packageName: - 'org.danilopianini.multi-jvm-test-plugin:org.danilopianini.multi-jvm-test-plugin.gradle.plugin', - managerData: { - fileReplacePosition: 822, - packageFile: 'gradle/libs.versions.toml', - }, - registryUrls: ['https://plugins.gradle.org/m2/'], - }, + { depName: 'org.sonarqube', depType: 'plugin' }, + { depName: 'io.jsonwebtoken:jjwt-api', depType: 'dependencies' }, ], }, + { + packageFile: 'buildSrc/build.gradle', + deps: [{ depType: 'devDependencies' }], + }, ]); }); - it('ignores an empty TOML', async () => { - const tomlFile = ''; - const fsMock = { - 'gradle/libs.versions.toml': tomlFile, - }; - mockFs(fsMock); - const res = await extractAllPackageFiles( - {} as ExtractConfig, - Object.keys(fsMock) - ); - expect(res).toBeNull(); + describe('registry URLs', () => { + it('deduplicates registry urls', async () => { + const fsMock = { + 'build.gradle': codeBlock` + repositories { maven { url "https://repo.maven.apache.org/maven2" } } + repositories { maven { url "https://repo.maven.apache.org/maven2" } } + repositories { maven { url "https://example.com" } } + repositories { maven { url "https://example.com" } } + plugins { id "foo.bar" version "1.2.3" } + dependencies { classpath "foo:bar:1.2.3" } + `, + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + + expect(res).toMatchObject([ + { + packageFile: 'build.gradle', + deps: [ + { + depType: 'plugin', + registryUrls: [ + 'https://repo.maven.apache.org/maven2', + 'https://example.com', + 'https://plugins.gradle.org/m2/', + ], + }, + { + registryUrls: [ + 'https://repo.maven.apache.org/maven2', + 'https://example.com', + ], + }, + ], + }, + ]); + }); + + it('interpolates registry URLs', async () => { + const buildFile = codeBlock` + repositories { + mavenCentral() + maven { + url = "\${repositoryBaseURL}/repository-build" + } + maven { + name = "baz" + url = "\${repositoryBaseURL}/\${name}" + } + } + + dependencies { + implementation "com.google.protobuf:protobuf-java:2.17.0" + } + `; + + const fsMock = { + 'build.gradle': buildFile, + 'gradle.properties': 'repositoryBaseURL: https\\://dummy.org/whatever', + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + + expect(res).toMatchObject([ + { + packageFile: 'gradle.properties', + deps: [], + }, + { + packageFile: 'build.gradle', + deps: [ + { + depName: 'com.google.protobuf:protobuf-java', + currentValue: '2.17.0', + managerData: { + fileReplacePosition: 262, + packageFile: 'build.gradle', + }, + fileReplacePosition: 262, + registryUrls: [ + 'https://repo.maven.apache.org/maven2', + 'https://dummy.org/whatever/repository-build', + 'https://dummy.org/whatever/baz', + ], + }, + ], + }, + ]); + }); }); - it('deletes commit message for plugins with version reference', async () => { - const tomlFile = ` - [versions] - detekt = "1.18.1" + describe('version catalogs', () => { + it('works with dependency catalogs', async () => { + const fsMock = { + 'gradle/libs.versions.toml': Fixtures.get('1/libs.versions.toml'), + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + expect(res).toMatchObject([ + { + packageFile: 'gradle/libs.versions.toml', + deps: [ + { + depName: 'io.gitlab.arturbosch.detekt:detekt-formatting', + groupName: 'detekt', + currentValue: '1.17.0', + managerData: { + fileReplacePosition: 21, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'io.kotest:kotest-assertions-core-jvm', + groupName: 'kotest', + currentValue: '4.6.0', + managerData: { + fileReplacePosition: 51, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'io.kotest:kotest-runner-junit5', + groupName: 'kotest', + currentValue: '4.6.0', + managerData: { + fileReplacePosition: 51, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'org.mockito:mockito-core', + groupName: 'org.mockito', + currentValue: '3.10.0', + managerData: { + fileReplacePosition: 474, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'com.github.siom79.japicmp:japicmp', + groupName: 'com.github.siom79.japicmp', + currentValue: '0.15.+', + managerData: { + fileReplacePosition: 561, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'guava', + skipReason: 'multiple-constraint-dep', + managerData: { + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'gson', + skipReason: 'unsupported-version', + managerData: { + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'io.gitlab.arturbosch.detekt', + depType: 'plugin', + currentValue: '1.17.0', + packageName: + 'io.gitlab.arturbosch.detekt:io.gitlab.arturbosch.detekt.gradle.plugin', + managerData: { + fileReplacePosition: 21, + packageFile: 'gradle/libs.versions.toml', + }, + registryUrls: ['https://plugins.gradle.org/m2/'], + }, + { + depName: 'org.danilopianini.publish-on-central', + depType: 'plugin', + currentValue: '0.5.0', + packageName: + 'org.danilopianini.publish-on-central:org.danilopianini.publish-on-central.gradle.plugin', + managerData: { + fileReplacePosition: 82, + packageFile: 'gradle/libs.versions.toml', + }, + registryUrls: ['https://plugins.gradle.org/m2/'], + }, + { + depName: 'org.ajoberstar.grgit', + depType: 'plugin', + commitMessageTopic: 'plugin grgit', + packageName: + 'org.ajoberstar.grgit:org.ajoberstar.grgit.gradle.plugin', + managerData: { + packageFile: 'gradle/libs.versions.toml', + }, + registryUrls: ['https://plugins.gradle.org/m2/'], + skipReason: 'unknown-version', + }, + ], + }, + ]); + }); - [plugins] - detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } + it('supports versions declared as single string', async () => { + const fsMock = { + 'gradle/libs.versions.toml': Fixtures.get('2/libs.versions.toml'), + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + + expect(res).toMatchObject([ + { + packageFile: 'gradle/libs.versions.toml', + deps: [ + { + depName: 'com.squareup.okhttp3:okhttp', + groupName: 'com.squareup.okhttp3', + currentValue: '4.9.0', + managerData: { + fileReplacePosition: 99, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'com.squareup.okio:okio', + groupName: 'com.squareup.okio', + currentValue: '2.8.0', + managerData: { + fileReplacePosition: 161, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'com.squareup.picasso:picasso', + groupName: 'com.squareup.picasso', + currentValue: '2.5.1', + managerData: { + fileReplacePosition: 243, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'com.squareup.retrofit2:retrofit', + groupName: 'retrofit', + currentValue: '2.8.2', + managerData: { + fileReplacePosition: 41, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'google-firebase-analytics', + managerData: { + packageFile: 'gradle/libs.versions.toml', + }, + skipReason: 'no-version', + }, + { + depName: 'google-firebase-crashlytics', + managerData: { + packageFile: 'gradle/libs.versions.toml', + }, + skipReason: 'no-version', + }, + { + depName: 'google-firebase-messaging', + managerData: { + packageFile: 'gradle/libs.versions.toml', + }, + skipReason: 'no-version', + }, + { + depName: 'org.jetbrains.kotlin.jvm', + depType: 'plugin', + currentValue: '1.5.21', + commitMessageTopic: 'plugin kotlinJvm', + packageName: + 'org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin', + managerData: { + fileReplacePosition: 661, + packageFile: 'gradle/libs.versions.toml', + }, + registryUrls: ['https://plugins.gradle.org/m2/'], + }, + { + depName: 'org.jetbrains.kotlin.plugin.serialization', + depType: 'plugin', + currentValue: '1.5.21', + packageName: + 'org.jetbrains.kotlin.plugin.serialization:org.jetbrains.kotlin.plugin.serialization.gradle.plugin', + managerData: { + fileReplacePosition: 21, + packageFile: 'gradle/libs.versions.toml', + }, + registryUrls: ['https://plugins.gradle.org/m2/'], + }, + { + depName: 'org.danilopianini.multi-jvm-test-plugin', + depType: 'plugin', + currentValue: '0.3.0', + commitMessageTopic: 'plugin multiJvm', + packageName: + 'org.danilopianini.multi-jvm-test-plugin:org.danilopianini.multi-jvm-test-plugin.gradle.plugin', + managerData: { + fileReplacePosition: 822, + packageFile: 'gradle/libs.versions.toml', + }, + registryUrls: ['https://plugins.gradle.org/m2/'], + }, + ], + }, + ]); + }); - [libraries] - detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } - `; - const fsMock = { - 'gradle/libs.versions.toml': tomlFile, - }; - mockFs(fsMock); - const res = await extractAllPackageFiles( - {} as ExtractConfig, - Object.keys(fsMock) - ); - expect(res).toMatchObject([ - { - packageFile: 'gradle/libs.versions.toml', - deps: [ - { - depName: 'io.gitlab.arturbosch.detekt:detekt-formatting', - groupName: 'detekt', - currentValue: '1.18.1', - managerData: { - fileReplacePosition: 30, - packageFile: 'gradle/libs.versions.toml', + it('ignores empty TOML file', async () => { + const fsMock = { + 'gradle/libs.versions.toml': '', + }; + mockFs(fsMock); + + expect( + await extractAllPackageFiles({} as ExtractConfig, Object.keys(fsMock)) + ).toBeNull(); + }); + + it('deletes commit message for plugins with version reference', async () => { + const fsMock = { + 'gradle/libs.versions.toml': stripIndent` + [versions] + detekt = "1.18.1" + + [plugins] + detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } + + [libraries] + detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } + `, + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + expect(res).toMatchObject([ + { + packageFile: 'gradle/libs.versions.toml', + deps: [ + { + depName: 'io.gitlab.arturbosch.detekt:detekt-formatting', + groupName: 'detekt', + currentValue: '1.18.1', + managerData: { + fileReplacePosition: 21, + packageFile: 'gradle/libs.versions.toml', + }, + fileReplacePosition: 21, }, - fileReplacePosition: 30, - registryUrls: [], - }, - { - depType: 'plugin', - depName: 'io.gitlab.arturbosch.detekt', - packageName: - 'io.gitlab.arturbosch.detekt:io.gitlab.arturbosch.detekt.gradle.plugin', - registryUrls: ['https://plugins.gradle.org/m2/'], - currentValue: '1.18.1', - managerData: { - fileReplacePosition: 30, - packageFile: 'gradle/libs.versions.toml', + { + depType: 'plugin', + depName: 'io.gitlab.arturbosch.detekt', + packageName: + 'io.gitlab.arturbosch.detekt:io.gitlab.arturbosch.detekt.gradle.plugin', + registryUrls: ['https://plugins.gradle.org/m2/'], + currentValue: '1.18.1', + managerData: { + fileReplacePosition: 21, + packageFile: 'gradle/libs.versions.toml', + }, + groupName: 'detekt', + fileReplacePosition: 21, }, - groupName: 'detekt', - fileReplacePosition: 30, - }, - ], - }, - ]); - }); + ], + }, + ]); + }); - it('should change the dependency version not the comment version', async () => { - const tomlFile = Fixtures.get('3/libs.versions.toml'); - const fsMock = { - 'gradle/libs.versions.toml': tomlFile, - }; - mockFs(fsMock); - const res = await extractAllPackageFiles( - {} as ExtractConfig, - Object.keys(fsMock) - ); - expect(res).toMatchObject([ - { - packageFile: 'gradle/libs.versions.toml', - datasource: 'maven', - deps: [ - { - depName: 'junit:junit', - groupName: 'junit', - currentValue: '1.4.9', - managerData: { + it('changes the dependency version, not the comment version', async () => { + const fsMock = { + 'gradle/libs.versions.toml': Fixtures.get('3/libs.versions.toml'), + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + expect(res).toMatchObject([ + { + packageFile: 'gradle/libs.versions.toml', + deps: [ + { + depName: 'junit:junit', + groupName: 'junit', + currentValue: '1.4.9', + managerData: { + fileReplacePosition: 124, + packageFile: 'gradle/libs.versions.toml', + }, fileReplacePosition: 124, - packageFile: 'gradle/libs.versions.toml', }, - fileReplacePosition: 124, - registryUrls: [], - }, - { - depName: 'mocha-junit:mocha-junit', - groupName: 'mocha-junit-reporter', - currentValue: '2.0.2', - managerData: { + { + depName: 'mocha-junit:mocha-junit', + groupName: 'mocha-junit-reporter', + currentValue: '2.0.2', + managerData: { + fileReplacePosition: 82, + packageFile: 'gradle/libs.versions.toml', + }, fileReplacePosition: 82, - packageFile: 'gradle/libs.versions.toml', }, - fileReplacePosition: 82, - registryUrls: [], - }, - ], - }, - ]); + ], + }, + ]); + }); }); - it('loads further scripts using apply from statements', async () => { - const buildFile = ` - buildscript { + describe('apply from', () => { + it('loads further scripts using apply from statements', async () => { + const buildFile = codeBlock` + buildscript { repositories { - mavenCentral() + mavenCentral() } apply from: "\${someDir}/libs1.gradle" @@ -627,408 +664,322 @@ describe('modules/manager/gradle/extract', () => { apply from: file("gradle/non-existing.gradle") dependencies { - classpath "com.google.protobuf:protobuf-java:\${protoBufVersion}" - classpath "com.google.guava:guava:\${guavaVersion}" - classpath "io.jsonwebtoken:jjwt-api:0.11.2" + classpath "com.google.protobuf:protobuf-java:\${protoBufVersion}" + classpath "com.google.guava:guava:\${guavaVersion}" + classpath "io.jsonwebtoken:jjwt-api:0.11.2" - classpath "org.junit.jupiter:junit-jupiter-api:\${junitVersion}" - classpath "org.junit.jupiter:junit-jupiter-engine:\${junitVersion}" - classpath "org.slf4j:slf4j-api:\${slf4jVersion}" + classpath "org.junit.jupiter:junit-jupiter-api:\${junitVersion}" + classpath "org.junit.jupiter:junit-jupiter-engine:\${junitVersion}" + classpath "org.slf4j:slf4j-api:\${slf4jVersion}" } - } - `; - - mockFs({ - 'gradleX/libs1.gradle': "ext.junitVersion = '5.5.2'", - 'gradle/libs2.gradle': "ext.protoBufVersion = '3.18.2'", - 'gradle/libs3.gradle': "ext.guavaVersion = '30.1-jre'", - 'gradleX/gradleX/libs4.gradle': "ext.slf4jVersion = '1.7.30'", - 'build.gradle': buildFile, - 'gradle.properties': 'someDir=gradleX', + } + `; + + const fsMock = { + 'gradleX/libs1.gradle': "ext.junitVersion = '5.5.2'", + 'gradle/libs2.gradle': "ext.protoBufVersion = '3.18.2'", + 'gradle/libs3.gradle': "ext.guavaVersion = '30.1-jre'", + 'gradleX/gradleX/libs4.gradle': "ext.slf4jVersion = '1.7.30'", + 'build.gradle': buildFile, + 'gradle.properties': 'someDir=gradleX', + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + + expect(res).toMatchObject([ + { packageFile: 'gradle.properties' }, + { + packageFile: 'build.gradle', + deps: [{ depName: 'io.jsonwebtoken:jjwt-api' }], + }, + { + packageFile: 'gradle/libs2.gradle', + deps: [ + { + depName: 'com.google.protobuf:protobuf-java', + currentValue: '3.18.2', + managerData: { packageFile: 'gradle/libs2.gradle' }, + }, + ], + }, + { + packageFile: 'gradle/libs3.gradle', + deps: [ + { + depName: 'com.google.guava:guava', + currentValue: '30.1-jre', + managerData: { packageFile: 'gradle/libs3.gradle' }, + }, + ], + }, + { + packageFile: 'gradleX/libs1.gradle', + deps: [ + { + depName: 'org.junit.jupiter:junit-jupiter-api', + currentValue: '5.5.2', + managerData: { packageFile: 'gradleX/libs1.gradle' }, + }, + { + depName: 'org.junit.jupiter:junit-jupiter-engine', + currentValue: '5.5.2', + managerData: { packageFile: 'gradleX/libs1.gradle' }, + }, + ], + }, + { + packageFile: 'gradleX/gradleX/libs4.gradle', + deps: [ + { + depName: 'org.slf4j:slf4j-api', + currentValue: '1.7.30', + managerData: { packageFile: 'gradleX/gradleX/libs4.gradle' }, + }, + ], + }, + ]); }); - const res = await extractAllPackageFiles({} as ExtractConfig, [ - 'gradleX/libs1.gradle', - 'gradle/libs2.gradle', - 'gradle/libs3.gradle', - 'gradleX/gradleX/libs4.gradle', - 'build.gradle', - 'gradle.properties', - ]); - - expect(res).toMatchObject([ - { packageFile: 'gradle.properties' }, - { - packageFile: 'build.gradle', - deps: [{ depName: 'io.jsonwebtoken:jjwt-api' }], - }, - { - packageFile: 'gradle/libs2.gradle', - deps: [ - { - depName: 'com.google.protobuf:protobuf-java', - currentValue: '3.18.2', - managerData: { packageFile: 'gradle/libs2.gradle' }, - }, - ], - }, - { - packageFile: 'gradle/libs3.gradle', - deps: [ - { - depName: 'com.google.guava:guava', - currentValue: '30.1-jre', - managerData: { packageFile: 'gradle/libs3.gradle' }, - }, - ], - }, - { - packageFile: 'gradleX/libs1.gradle', - deps: [ - { - depName: 'org.junit.jupiter:junit-jupiter-api', - currentValue: '5.5.2', - managerData: { packageFile: 'gradleX/libs1.gradle' }, - }, - { - depName: 'org.junit.jupiter:junit-jupiter-engine', - currentValue: '5.5.2', - managerData: { packageFile: 'gradleX/libs1.gradle' }, - }, - ], - }, - { - packageFile: 'gradleX/gradleX/libs4.gradle', - deps: [ - { - depName: 'org.slf4j:slf4j-api', - currentValue: '1.7.30', - managerData: { packageFile: 'gradleX/gradleX/libs4.gradle' }, - }, - ], - }, - ]); - }); - - it('apply from works with files in sub-directories', async () => { - const buildFile = ` - buildscript { - repositories { - mavenCentral() - } - - apply from: "gradle/libs4.gradle" - - dependencies { - classpath "com.google.protobuf:protobuf-java:\${protoBufVersion}" - } - } - `; - - mockFs({ - 'somesubdir/gradle/libs4.gradle': "ext.protoBufVersion = '3.18.2'", - 'somesubdir/build.gradle': buildFile, + it('works with files in sub-directories', async () => { + const buildFile = ` + buildscript { + repositories { + mavenCentral() + } + + apply from: "gradle/libs4.gradle" + + dependencies { + classpath "com.google.protobuf:protobuf-java:\${protoBufVersion}" + } + } + `; + + const fsMock = { + 'somesubdir/gradle/libs4.gradle': "ext.protoBufVersion = '3.18.2'", + 'somesubdir/build.gradle': buildFile, + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + + expect(res).toMatchObject([ + { packageFile: 'somesubdir/build.gradle' }, + { + packageFile: 'somesubdir/gradle/libs4.gradle', + deps: [{ depName: 'com.google.protobuf:protobuf-java' }], + }, + ]); }); - const res = await extractAllPackageFiles({} as ExtractConfig, [ - 'somesubdir/gradle/libs4.gradle', - 'somesubdir/build.gradle', - ]); - - expect(res).toMatchObject([ - { packageFile: 'somesubdir/build.gradle' }, - { - packageFile: 'somesubdir/gradle/libs4.gradle', - deps: [{ depName: 'com.google.protobuf:protobuf-java' }], - }, - ]); - }); + it('prevents recursive apply from calls', async () => { + const fsMock = { + 'build.gradle': "apply from: 'test.gradle'", + 'test.gradle': "apply from: 'build.gradle'", + }; + mockFs(fsMock); - it('prevents recursive apply from calls', async () => { - mockFs({ - 'build.gradle': "apply from: 'test.gradle'", - 'test.gradle': "apply from: 'build.gradle'", + expect( + await extractAllPackageFiles({} as ExtractConfig, Object.keys(fsMock)) + ).toBeNull(); }); - const res = await extractAllPackageFiles({} as ExtractConfig, [ - 'build.gradle', - 'test.gradle', - ]); - - expect(res).toBeNull(); - }); + it('prevents inclusion of non-Gradle files', async () => { + const fsMock = { + 'build.gradle': "apply from: '../../test.non-gradle'", + }; + mockFs(fsMock); - it('prevents inclusion of non-Gradle files', async () => { - mockFs({ - 'build.gradle': "apply from: '../../test.non-gradle'", + expect( + await extractAllPackageFiles({} as ExtractConfig, Object.keys(fsMock)) + ).toBeNull(); }); - - const res = await extractAllPackageFiles({} as ExtractConfig, [ - 'build.gradle', - ]); - - expect(res).toBeNull(); - }); - - it('filters duplicate dependency findings', async () => { - const buildFile = ` - apply from: 'test.gradle' - - repositories { - mavenCentral() - } - - dependencies { - implementation "io.jsonwebtoken:jjwt-api:$\{jjwtVersion}" - runtimeOnly "io.jsonwebtoken:jjwt-impl:$\{jjwtVersion}" - } - `; - - const testFile = ` - ext.jjwtVersion = '0.11.2' - - ext { - jjwtApi = "io.jsonwebtoken:jjwt-api:$jjwtVersion" - } - `; - - mockFs({ - 'build.gradle': buildFile, - 'test.gradle': testFile, - }); - - const res = await extractAllPackageFiles({} as ExtractConfig, [ - 'build.gradle', - 'test.gradle', - ]); - - expect(res).toMatchObject([ - { - packageFile: 'test.gradle', - deps: [ - { depName: 'io.jsonwebtoken:jjwt-api' }, - { depName: 'io.jsonwebtoken:jjwt-impl' }, - ], - }, - { packageFile: 'build.gradle' }, - ]); }); - it('ensures depType is assigned', async () => { - const fsMock = { - 'build.gradle': - "id 'org.sonarqube' version '3.1.1'\n\"io.jsonwebtoken:jjwt-api:0.11.2\"", - 'buildSrc/build.gradle': '"com.google.protobuf:protobuf-java:3.18.2"', - }; - - mockFs(fsMock); - - const res = await extractAllPackageFiles( - {} as ExtractConfig, - Object.keys(fsMock) - ); - - expect(res).toMatchObject([ - { - packageFile: 'build.gradle', - deps: [ - { depName: 'org.sonarqube', depType: 'plugin' }, - { depName: 'io.jsonwebtoken:jjwt-api', depType: 'dependencies' }, - ], - }, - { - packageFile: 'buildSrc/build.gradle', - deps: [{ depType: 'devDependencies' }], - }, - ]); - }); - - // Tests for gradle-consistent-version plugin - it('gradle-consistent-versions parse versions files', async () => { - const fsMock = { - 'versions.props': `org.apache.lucene:* = 1.2.3`, - 'versions.lock': stripIndent` - # Run ./gradlew --write-locks to regenerate this file - org.apache.lucene:lucene-core:1.2.3 (10 constraints: 95be0c15) - org.apache.lucene:lucene-codecs:1.2.3 (5 constraints: 1231231) - `, - }; - - mockFs(fsMock); - - const res = await extractAllPackageFiles( - {} as ExtractConfig, - Object.keys(fsMock) - ); - - expect(res).toMatchObject([ - { - packageFile: 'versions.lock', - }, - { - packageFile: 'versions.props', - deps: [ - { - depName: 'org.apache.lucene:lucene-core', - depType: 'dependencies', - fileReplacePosition: 22, - groupName: 'org.apache.lucene:*', - lockedVersion: '1.2.3', - managerData: { + describe('gradle-consistent-versions plugin', () => { + it('parses versions files', async () => { + const fsMock = { + 'versions.props': `org.apache.lucene:* = 1.2.3`, + 'versions.lock': stripIndent` + # Run ./gradlew --write-locks to regenerate this file + org.apache.lucene:lucene-core:1.2.3 (10 constraints: 95be0c15) + org.apache.lucene:lucene-codecs:1.2.3 (5 constraints: 1231231) + `, + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + + expect(res).toMatchObject([ + { + packageFile: 'versions.lock', + }, + { + packageFile: 'versions.props', + deps: [ + { + depName: 'org.apache.lucene:lucene-core', + depType: 'dependencies', fileReplacePosition: 22, - packageFile: 'versions.props', - }, - registryUrls: [], - }, - { - depName: 'org.apache.lucene:lucene-codecs', - depType: 'dependencies', - fileReplacePosition: 22, - groupName: 'org.apache.lucene:*', - lockedVersion: '1.2.3', - managerData: { + groupName: 'org.apache.lucene:*', + lockedVersion: '1.2.3', + managerData: { + fileReplacePosition: 22, + packageFile: 'versions.props', + }, + }, + { + depName: 'org.apache.lucene:lucene-codecs', + depType: 'dependencies', fileReplacePosition: 22, - packageFile: 'versions.props', - }, - registryUrls: [], - }, - ], - }, - ]); - }); - - it('gradle-consistent-versions plugin not used due to lockfile not a GCV lockfile', async () => { - const fsMock = { - 'versions.props': `org.apache.lucene:* = 1.2.3`, - 'versions.lock': stripIndent` - This is NOT a lock file - `, - }; - mockFs(fsMock); - - const res = await extractAllPackageFiles( - {} as ExtractConfig, - Object.keys(fsMock) - ); - expect(res).toBeNull(); - }); - - it('gradle-consistent-versions plugin not used due to lockfile missing', async () => { - const fsMock = { - 'build.gradle': '(this file contains) com.palantir.consistent-versions', - 'versions.props': `org.apache.lucene:* = 1.2.3`, - }; - mockFs(fsMock); - - const res = await extractAllPackageFiles( - {} as ExtractConfig, - Object.keys(fsMock) - ); - expect(res).toBeNull(); - }); + groupName: 'org.apache.lucene:*', + lockedVersion: '1.2.3', + managerData: { + fileReplacePosition: 22, + packageFile: 'versions.props', + }, + }, + ], + }, + ]); + }); - it('gradle-consistent-versions multi levels of glob', async () => { - const fsMock = { - 'versions.props': stripIndent` - org.apache.* = 4 - org.apache.lucene:* = 3 - org.apache.lucene:a.* = 2 - org.apache.lucene:a.b = 1 - `, - 'versions.lock': stripIndent` - # Run ./gradlew --write-locks to regenerate this file - org.apache.solr:x.y:1 (10 constraints: 95be0c15) - org.apache.lucene:a.b:1 (10 constraints: 95be0c15) - org.apache.lucene:a.c:1 (10 constraints: 95be0c15) - org.apache.lucene:a.d:1 (10 constraints: 95be0c15) - org.apache.lucene:d:1 (10 constraints: 95be0c15) - org.apache.lucene:e.f:1 (10 constraints: 95be0c15) - `, - }; - mockFs(fsMock); + it('plugin not used due to lockfile not a GCV lockfile', async () => { + const fsMock = { + 'versions.props': `org.apache.lucene:* = 1.2.3`, + 'versions.lock': stripIndent` + This is NOT a lock file + `, + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + expect(res).toBeNull(); + }); - const res = await extractAllPackageFiles( - {} as ExtractConfig, - Object.keys(fsMock) - ); + it('plugin not used due to lockfile missing', async () => { + const fsMock = { + 'build.gradle': '(this file contains) com.palantir.consistent-versions', + 'versions.props': `org.apache.lucene:* = 1.2.3`, + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + expect(res).toBeNull(); + }); - // Each lock dep is only present once, with highest prio for exact prop match, then globs from longest to shortest - expect(res).toMatchObject([ - { - packageFile: 'versions.lock', - datasource: 'maven', - deps: [], - }, - { - packageFile: 'versions.props', - datasource: 'maven', - deps: [ - { - managerData: { - packageFile: 'versions.props', + it('supports multiple levels of glob', async () => { + const fsMock = { + 'versions.props': stripIndent` + org.apache.* = 4 + org.apache.lucene:* = 3 + org.apache.lucene:a.* = 2 + org.apache.lucene:a.b = 1 + `, + 'versions.lock': stripIndent` + # Run ./gradlew --write-locks to regenerate this file + org.apache.solr:x.y:1 (10 constraints: 95be0c15) + org.apache.lucene:a.b:1 (10 constraints: 95be0c15) + org.apache.lucene:a.c:1 (10 constraints: 95be0c15) + org.apache.lucene:a.d:1 (10 constraints: 95be0c15) + org.apache.lucene:d:1 (10 constraints: 95be0c15) + org.apache.lucene:e.f:1 (10 constraints: 95be0c15) + `, + }; + mockFs(fsMock); + + const res = await extractAllPackageFiles( + {} as ExtractConfig, + Object.keys(fsMock) + ); + + // Each lock dep is only present once, with highest prio for exact prop match, then globs from longest to shortest + expect(res).toMatchObject([ + { + packageFile: 'versions.lock', + deps: [], + }, + { + packageFile: 'versions.props', + deps: [ + { + managerData: { + packageFile: 'versions.props', + fileReplacePosition: 91, + }, + depName: 'org.apache.lucene:a.b', + currentValue: '1', + lockedVersion: '1', fileReplacePosition: 91, - }, - depName: 'org.apache.lucene:a.b', - currentValue: '1', - lockedVersion: '1', - fileReplacePosition: 91, - registryUrls: [], - depType: 'dependencies', - }, - { - managerData: { - packageFile: 'versions.props', + depType: 'dependencies', + }, + { + managerData: { + packageFile: 'versions.props', + fileReplacePosition: 65, + }, + depName: 'org.apache.lucene:a.c', + currentValue: '2', + lockedVersion: '1', + groupName: 'org.apache.lucene:a.*', fileReplacePosition: 65, - }, - depName: 'org.apache.lucene:a.c', - currentValue: '2', - lockedVersion: '1', - groupName: 'org.apache.lucene:a.*', - fileReplacePosition: 65, - registryUrls: [], - depType: 'dependencies', - }, - { - managerData: { - packageFile: 'versions.props', + depType: 'dependencies', + }, + { + managerData: { + packageFile: 'versions.props', + fileReplacePosition: 65, + }, + depName: 'org.apache.lucene:a.d', + currentValue: '2', + lockedVersion: '1', + groupName: 'org.apache.lucene:a.*', fileReplacePosition: 65, - }, - depName: 'org.apache.lucene:a.d', - currentValue: '2', - lockedVersion: '1', - groupName: 'org.apache.lucene:a.*', - fileReplacePosition: 65, - registryUrls: [], - depType: 'dependencies', - }, - { - managerData: { - packageFile: 'versions.props', + depType: 'dependencies', + }, + { + managerData: { + packageFile: 'versions.props', + fileReplacePosition: 39, + }, + depName: 'org.apache.lucene:d', + currentValue: '3', + lockedVersion: '1', + groupName: 'org.apache.lucene:*', fileReplacePosition: 39, - }, - depName: 'org.apache.lucene:d', - currentValue: '3', - lockedVersion: '1', - groupName: 'org.apache.lucene:*', - fileReplacePosition: 39, - registryUrls: [], - depType: 'dependencies', - }, - { - managerData: { - packageFile: 'versions.props', + depType: 'dependencies', + }, + { + managerData: { + packageFile: 'versions.props', + fileReplacePosition: 39, + }, + depName: 'org.apache.lucene:e.f', + currentValue: '3', + lockedVersion: '1', + groupName: 'org.apache.lucene:*', fileReplacePosition: 39, + depType: 'dependencies', }, - depName: 'org.apache.lucene:e.f', - currentValue: '3', - lockedVersion: '1', - groupName: 'org.apache.lucene:*', - fileReplacePosition: 39, - registryUrls: [], - depType: 'dependencies', - }, - ], - }, - ]); + ], + }, + ]); + }); }); }); diff --git a/lib/modules/manager/gradle/extract.ts b/lib/modules/manager/gradle/extract.ts index 01bc483297..f3ff86968d 100644 --- a/lib/modules/manager/gradle/extract.ts +++ b/lib/modules/manager/gradle/extract.ts @@ -26,29 +26,14 @@ import { const datasource = MavenDatasource.id; -// Enables reverse sorting in generateBranchConfig() -// -// Required for grouped dependencies to be upgraded -// correctly in single branch. -// -// https://github.com/renovatebot/renovate/issues/8224 -function elevateFileReplacePositionField( - deps: PackageDependency<GradleManagerData>[] -): PackageDependency<GradleManagerData>[] { - return deps.map((dep) => ({ - ...dep, - fileReplacePosition: dep?.managerData?.fileReplacePosition, - })); -} - export async function extractAllPackageFiles( config: ExtractConfig, packageFiles: string[] ): Promise<PackageFile[] | null> { const extractedDeps: PackageDependency<GradleManagerData>[] = []; - const registry: VariableRegistry = {}; + const varRegistry: VariableRegistry = {}; const packageFilesByName: Record<string, PackageFile> = {}; - const registryUrls: string[] = []; + const packageRegistries: string[] = []; const reorderedFiles = reorderFiles(packageFiles); const fileContents = await getFileContentMap(packageFiles, true); @@ -62,11 +47,11 @@ export async function extractAllPackageFiles( try { // TODO #7154 const content = fileContents[packageFile]!; - const dir = upath.dirname(toAbsolutePath(packageFile)); + const packageFileDir = upath.dirname(toAbsolutePath(packageFile)); const updateVars = (newVars: PackageVariables): void => { - const oldVars = registry[dir] || {}; - registry[dir] = { ...oldVars, ...newVars }; + const oldVars = varRegistry[packageFileDir] || {}; + varRegistry[packageFileDir] = { ...oldVars, ...newVars }; }; if (isPropsFile(packageFile)) { @@ -83,18 +68,21 @@ export async function extractAllPackageFiles( const updatesFromGcv = parseGcv(packageFile, fileContents); extractedDeps.push(...updatesFromGcv); } else if (isGradleScriptFile(packageFile)) { - const vars = getVars(registry, dir); + const vars = getVars(varRegistry, packageFileDir); const { deps, urls, vars: gradleVars, } = parseGradle(content, vars, packageFile, fileContents); - urls.forEach((url) => { - if (!registryUrls.includes(url)) { - registryUrls.push(url); + for (const url of urls) { + if (!packageRegistries.includes(url)) { + packageRegistries.push(url); } - }); - registry[dir] = { ...registry[dir], ...gradleVars }; + } + varRegistry[packageFileDir] = { + ...varRegistry[packageFileDir], + ...gradleVars, + }; updateVars(gradleVars); extractedDeps.push(...deps); } @@ -110,22 +98,24 @@ export async function extractAllPackageFiles( return null; } - elevateFileReplacePositionField(extractedDeps).forEach((dep) => { + for (const dep of extractedDeps) { + dep.fileReplacePosition = dep?.managerData?.fileReplacePosition; // #8224 + const key = dep.managerData?.packageFile; // istanbul ignore else if (key) { - let pkgFile = packageFilesByName[key]; + let pkgFile: PackageFile = packageFilesByName[key]; // istanbul ignore if: won't happen if "apply from" processes only initially known files if (!pkgFile) { pkgFile = { packageFile: key, datasource, deps: [], - } as PackageFile; + }; } dep.registryUrls = [ - ...new Set([...registryUrls, ...(dep.registryUrls ?? [])]), + ...new Set([...packageRegistries, ...(dep.registryUrls ?? [])]), ]; if (!dep.depType) { @@ -148,8 +138,7 @@ export async function extractAllPackageFiles( } else { logger.warn({ dep }, `Failed to process Gradle dependency`); } - }); + } - const result = Object.values(packageFilesByName); - return result; + return Object.values(packageFilesByName); } diff --git a/lib/modules/manager/gradle/parser.ts b/lib/modules/manager/gradle/parser.ts index 5c2a486251..ec245fa0c0 100644 --- a/lib/modules/manager/gradle/parser.ts +++ b/lib/modules/manager/gradle/parser.ts @@ -49,7 +49,7 @@ export function parseGradle( globalVars: initVars, deps: [], - depRegistryUrls: [], + registryUrls: [], varTokens: [], tmpTokenStore: {}, @@ -59,7 +59,7 @@ export function parseGradle( if (parsedResult) { deps.push(...parsedResult.deps); vars = { ...vars, ...parsedResult.globalVars }; - urls.push(...parsedResult.depRegistryUrls); + urls.push(...parsedResult.registryUrls); } return { deps, urls, vars }; diff --git a/lib/modules/manager/gradle/parser/common.spec.ts b/lib/modules/manager/gradle/parser/common.spec.ts index 99579709c0..c1b6879406 100644 --- a/lib/modules/manager/gradle/parser/common.spec.ts +++ b/lib/modules/manager/gradle/parser/common.spec.ts @@ -23,7 +23,7 @@ describe('modules/manager/gradle/parser/common', () => { globalVars: {}, deps: [], - depRegistryUrls: [], + registryUrls: [], varTokens: [], tmpTokenStore: {}, diff --git a/lib/modules/manager/gradle/parser/handlers.ts b/lib/modules/manager/gradle/parser/handlers.ts index 2ab626254c..043a896ddc 100644 --- a/lib/modules/manager/gradle/parser/handlers.ts +++ b/lib/modules/manager/gradle/parser/handlers.ts @@ -248,7 +248,7 @@ export function handlePlugin(ctx: Ctx): Ctx { export function handlePredefinedRegistryUrl(ctx: Ctx): Ctx { const registryName = loadFromTokenMap(ctx, 'registryUrl')[0].value; - ctx.depRegistryUrls.push( + ctx.registryUrls.push( REGISTRY_URLS[registryName as keyof typeof REGISTRY_URLS] ); @@ -281,7 +281,7 @@ export function handleCustomRegistryUrl(ctx: Ctx): Ctx { try { const { host, protocol } = url.parse(registryUrl); if (host && protocol) { - ctx.depRegistryUrls.push(registryUrl); + ctx.registryUrls.push(registryUrl); } } catch (e) { // no-op @@ -370,7 +370,7 @@ export function handleApplyFrom(ctx: Ctx): Ctx { ctx.deps.push(...matchResult.deps); ctx.globalVars = { ...ctx.globalVars, ...matchResult.vars }; - ctx.depRegistryUrls.push(...matchResult.urls); + ctx.registryUrls.push(...matchResult.urls); return ctx; } diff --git a/lib/modules/manager/gradle/types.ts b/lib/modules/manager/gradle/types.ts index b455142ce8..d68c39cd30 100644 --- a/lib/modules/manager/gradle/types.ts +++ b/lib/modules/manager/gradle/types.ts @@ -74,7 +74,7 @@ export interface Ctx { globalVars: PackageVariables; deps: PackageDependency<GradleManagerData>[]; - depRegistryUrls: string[]; + registryUrls: string[]; varTokens: lexer.Token[]; tmpTokenStore: Record<string, lexer.Token[]>; -- GitLab