diff --git a/lib/modules/manager/ant/extract.spec.ts b/lib/modules/manager/ant/extract.spec.ts index fe36d2e35b8..d86415edad3 100644 --- a/lib/modules/manager/ant/extract.spec.ts +++ b/lib/modules/manager/ant/extract.spec.ts @@ -626,6 +626,138 @@ describe('modules/manager/ant/extract', () => { ]); }); + it('follows import file references', async () => { + fs.readLocalFile.mockImplementation((fileName: string) => { + const files: Record = { + 'build.xml': codeBlock` + + + + `, + 'deps.xml': codeBlock` + + + + + + `, + }; + return Promise.resolve(files[fileName] ?? null); + }); + + const result = await extractAllPackageFiles({}, ['build.xml']); + + expect(result).toEqual([ + { + packageFile: 'deps.xml', + deps: [ + expect.objectContaining({ + depName: 'junit:junit', + currentValue: '4.13.2', + }), + ], + }, + ]); + }); + + it('skips missing import files', async () => { + fs.readLocalFile.mockImplementation((fileName: string) => { + const files: Record = { + 'build.xml': codeBlock` + + + + + + + `, + }; + return Promise.resolve(files[fileName] ?? null); + }); + + const result = await extractAllPackageFiles({}, ['build.xml']); + + expect(result).toEqual([ + { + packageFile: 'build.xml', + deps: [ + expect.objectContaining({ + depName: 'junit:junit', + currentValue: '4.13.2', + }), + ], + }, + ]); + }); + + it('does not loop on self-importing files', async () => { + fs.readLocalFile.mockImplementation((fileName: string) => { + const files: Record = { + 'build.xml': codeBlock` + + + + + + + `, + }; + return Promise.resolve(files[fileName] ?? null); + }); + + const result = await extractAllPackageFiles({}, ['build.xml']); + + expect(result).toEqual([ + { + packageFile: 'build.xml', + deps: [ + expect.objectContaining({ + depName: 'junit:junit', + currentValue: '4.13.2', + }), + ], + }, + ]); + }); + + it('shares properties across imported files', async () => { + fs.readLocalFile.mockImplementation((fileName: string) => { + const files: Record = { + 'build.xml': codeBlock` + + + + + `, + 'deps.xml': codeBlock` + + + + + + `, + }; + return Promise.resolve(files[fileName] ?? null); + }); + + const result = await extractAllPackageFiles({}, ['build.xml']); + + expect(fs.readLocalFile).toHaveBeenCalledWith('deps.xml', 'utf8'); + expect(result).toEqual([ + { + packageFile: 'build.xml', + deps: [ + expect.objectContaining({ + depName: 'junit:junit', + currentValue: '4.13.2', + sharedVariableName: 'junit.version', + editFile: 'build.xml', + }), + ], + }, + ]); + }); + it('handles chain referencing undefined property', async () => { fs.readLocalFile.mockResolvedValue(codeBlock` diff --git a/lib/modules/manager/ant/extract.ts b/lib/modules/manager/ant/extract.ts index 529f4636699..6a0cb4a9f85 100644 --- a/lib/modules/manager/ant/extract.ts +++ b/lib/modules/manager/ant/extract.ts @@ -158,6 +158,11 @@ async function walkNodeInOrder( } } } + } else if (child.name === 'import' && child.attr.file) { + const importedFile = upath.normalize( + upath.join(baseDir, child.attr.file), + ); + await walkXmlFile(importedFile, visitedFiles, allProps, allRawDeps); } else if (child.name === 'dependency') { const rawDep = collectDependency(child, packageFile, content); if (rawDep) { @@ -182,6 +187,9 @@ async function walkXmlFile( allProps: Record, allRawDeps: RawDep[], ): Promise { + if (visitedFiles.has(packageFile)) { + return; + } visitedFiles.add(packageFile); const content = await readLocalFile(packageFile, 'utf8');