diff --git a/lib/modules/manager/pep621/extract.spec.ts b/lib/modules/manager/pep621/extract.spec.ts index 37bd318ea6e550..049397b4f0c49a 100644 --- a/lib/modules/manager/pep621/extract.spec.ts +++ b/lib/modules/manager/pep621/extract.spec.ts @@ -605,5 +605,25 @@ describe('modules/manager/pep621/extract', () => { ], }); }); + + it('should resolve dependencies with template', async () => { + const content = codeBlock` + [project] + name = "{{ name }}" + dynamic = ["version"] + requires-python = ">=3.7" + license = {text = "MIT"} + {# comment #} + dependencies = [ + "blinker", + {% if foo %} + "packaging>=20.9,!=22.0", + {% endif %} + ] + readme = "README.md" + `; + const res = await extractPackageFile(content, 'pyproject.toml'); + expect(res?.deps).toHaveLength(2); + }); }); }); diff --git a/lib/modules/manager/pep621/utils.ts b/lib/modules/manager/pep621/utils.ts index faf88e2006cc29..9b6dde9bfbedd6 100644 --- a/lib/modules/manager/pep621/utils.ts +++ b/lib/modules/manager/pep621/utils.ts @@ -127,7 +127,7 @@ export function parsePyProject( content: string, ): PyProject | null { try { - const jsonMap = parseToml(content); + const jsonMap = parseToml(content, { removeTemplates: true }); return PyProjectSchema.parse(jsonMap); } catch (err) { logger.debug( diff --git a/lib/modules/manager/poetry/artifacts.ts b/lib/modules/manager/poetry/artifacts.ts index d5582f619c5910..5888c7dbeee9a4 100644 --- a/lib/modules/manager/poetry/artifacts.ts +++ b/lib/modules/manager/poetry/artifacts.ts @@ -95,7 +95,7 @@ export function getPoetryRequirement( function getPoetrySources(content: string, fileName: string): PoetrySource[] { let pyprojectFile: PoetryFile; try { - pyprojectFile = parseToml(content) as PoetryFile; + pyprojectFile = parseToml(content, { removeTemplates: true }) as PoetryFile; } catch (err) { logger.debug({ err }, 'Error parsing pyproject.toml file'); return []; diff --git a/lib/modules/manager/poetry/extract.spec.ts b/lib/modules/manager/poetry/extract.spec.ts index 5801cd73c93e6b..59b1f65fff531c 100644 --- a/lib/modules/manager/poetry/extract.spec.ts +++ b/lib/modules/manager/poetry/extract.spec.ts @@ -496,5 +496,23 @@ describe('modules/manager/poetry/extract', () => { ]); }); }); + + it('parses package file with template', async () => { + const content = codeBlock` + [tool.poetry] + name = "{{ name }}" + + {# comment #} + [tool.poetry.dependencies] + python = "^3.9" + {% if foo %} + dep1 = "^1.0.0" + {% endif %} + `; + const res = await extractPackageFile(content, filename); + expect(res?.deps).toHaveLength(2); + expect(res?.deps[0].depName).toBe('python'); + expect(res?.deps[1].depName).toBe('dep1'); + }); }); }); diff --git a/lib/util/schema-utils.ts b/lib/util/schema-utils.ts index de637250c8f9cf..d4843358ddbd2f 100644 --- a/lib/util/schema-utils.ts +++ b/lib/util/schema-utils.ts @@ -258,7 +258,7 @@ export const MultidocYaml = z.string().transform((str, ctx): JsonArray => { export const Toml = z.string().transform((str, ctx) => { try { - return parseToml(str); + return parseToml(str, { removeTemplates: true }); } catch { ctx.addIssue({ code: 'custom', message: 'Invalid TOML' }); return z.NEVER; diff --git a/lib/util/toml.spec.ts b/lib/util/toml.spec.ts index b219c42b659158..514dd9ad02aa8c 100644 --- a/lib/util/toml.spec.ts +++ b/lib/util/toml.spec.ts @@ -28,4 +28,25 @@ describe('util/toml', () => { expect(() => parseToml(input)).toThrow(SyntaxError); }); + + it('should parse content with templates', () => { + const input = codeBlock` + [project] + name = "{{ value }}" + + {# comment #} + [tool.poetry.dependencies] + {% if enabled %} + python = "^3.12" + {%+ endif -%} + `; + expect(parseToml(input, { removeTemplates: true })).toStrictEqual({ + project: { name: '' }, + tool: { + poetry: { + dependencies: { python: '^3.12' }, + }, + }, + }); + }); }); diff --git a/lib/util/toml.ts b/lib/util/toml.ts index 51814334a56de6..5cfa86ac064af7 100644 --- a/lib/util/toml.ts +++ b/lib/util/toml.ts @@ -1,6 +1,22 @@ import { getStaticTOMLValue, parseTOML } from 'toml-eslint-parser'; +import { regEx } from './regex'; -export function parse(input: string): unknown { - const ast = parseTOML(input); +export interface TomlOptions { + removeTemplates?: boolean; +} + +export function parse(input: string, options?: TomlOptions): unknown { + const massagedContent = massageContent(input, options); + const ast = parseTOML(massagedContent); return getStaticTOMLValue(ast); } + +function massageContent(content: string, options?: TomlOptions): string { + if (options?.removeTemplates) { + return content + .replaceAll(regEx(/{%.+?%}/gs), '') + .replaceAll(regEx(/{{.+?}}/gs), '') + .replaceAll(regEx(/{#.+?#}/gs), ''); + } + return content; +}