From d2eb48d1a4446266768e49ff833e7c891721005b Mon Sep 17 00:00:00 2001
From: Sergei Zharinov <zharinov@users.noreply.github.com>
Date: Mon, 3 Mar 2025 14:35:16 -0300
Subject: [PATCH] fix(poetry): Template keys handling (#34460)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 lib/modules/manager/pep621/utils.ts        |  5 ++---
 lib/modules/manager/poetry/artifacts.ts    |  9 ++++-----
 lib/modules/manager/poetry/extract.spec.ts |  1 +
 lib/modules/manager/poetry/extract.ts      |  4 ++--
 lib/util/toml.spec.ts                      | 18 +++++++++++++++++-
 lib/util/toml.ts                           |  6 ++++++
 6 files changed, 32 insertions(+), 11 deletions(-)

diff --git a/lib/modules/manager/pep621/utils.ts b/lib/modules/manager/pep621/utils.ts
index a53f953080..178239a065 100644
--- a/lib/modules/manager/pep621/utils.ts
+++ b/lib/modules/manager/pep621/utils.ts
@@ -1,8 +1,7 @@
 import is from '@sindresorhus/is';
 import { logger } from '../../../logger';
 import { regEx } from '../../../util/regex';
-import { stripTemplates } from '../../../util/string';
-import { parse as parseToml } from '../../../util/toml';
+import { massage as massageToml, parse as parseToml } from '../../../util/toml';
 import { PypiDatasource } from '../../datasource/pypi';
 import { normalizePythonDepName } from '../../datasource/pypi/common';
 import type { PackageDependency } from '../types';
@@ -137,7 +136,7 @@ export function parsePyProject(
   content: string,
 ): PyProject | null {
   try {
-    const jsonMap = parseToml(stripTemplates(content));
+    const jsonMap = parseToml(massageToml(content));
     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 d75a669344..6e1b2b5aae 100644
--- a/lib/modules/manager/poetry/artifacts.ts
+++ b/lib/modules/manager/poetry/artifacts.ts
@@ -16,8 +16,7 @@ import { getGitEnvironmentVariables } from '../../../util/git/auth';
 import { find } from '../../../util/host-rules';
 import { regEx } from '../../../util/regex';
 import { Result } from '../../../util/result';
-import { stripTemplates } from '../../../util/string';
-import { parse as parseToml } from '../../../util/toml';
+import { massage as massageToml, parse as parseToml } from '../../../util/toml';
 import { parseUrl } from '../../../util/url';
 import { PypiDatasource } from '../../datasource/pypi';
 import { getGoogleAuthHostRule } from '../../datasource/util';
@@ -31,7 +30,7 @@ export function getPythonConstraint(
 ): string | null {
   // Read Python version from `pyproject.toml` first as it could have been updated
   const pyprojectPythonConstraint = Result.parse(
-    pyProjectContent,
+    massageToml(pyProjectContent),
     PoetrySchemaToml.transform(
       ({ packageFileContent }) =>
         packageFileContent.deps.find((dep) => dep.depName === 'python')
@@ -82,7 +81,7 @@ export function getPoetryRequirement(
   }
 
   const { val: pyprojectPoetryConstraint } = Result.parse(
-    pyProjectContent,
+    massageToml(pyProjectContent),
     PoetrySchemaToml.transform(({ poetryRequirement }) => poetryRequirement),
   ).unwrap();
   if (pyprojectPoetryConstraint) {
@@ -98,7 +97,7 @@ export function getPoetryRequirement(
 function getPoetrySources(content: string, fileName: string): PoetrySource[] {
   let pyprojectFile: PoetryFile;
   try {
-    pyprojectFile = parseToml(stripTemplates(content)) as PoetryFile;
+    pyprojectFile = parseToml(massageToml(content)) 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 cbfa6d4ba7..aa0d7da86a 100644
--- a/lib/modules/manager/poetry/extract.spec.ts
+++ b/lib/modules/manager/poetry/extract.spec.ts
@@ -504,6 +504,7 @@ describe('modules/manager/poetry/extract', () => {
             {# comment #}
             [tool.poetry.dependencies]
             python = "^3.9"
+            {{ foo }} = "{{ bar }}"
             {% if foo %}
             dep1 = "^1.0.0"
             {% endif %}
diff --git a/lib/modules/manager/poetry/extract.ts b/lib/modules/manager/poetry/extract.ts
index 71fb2f0049..f13130e610 100644
--- a/lib/modules/manager/poetry/extract.ts
+++ b/lib/modules/manager/poetry/extract.ts
@@ -7,7 +7,7 @@ import {
   readLocalFile,
 } from '../../../util/fs';
 import { Result } from '../../../util/result';
-import { stripTemplates } from '../../../util/string';
+import { massage as massageToml } from '../../../util/toml';
 import { GithubReleasesDatasource } from '../../datasource/github-releases';
 import type { PackageFileContent } from '../types';
 import { Lockfile, PoetrySchemaToml } from './schema';
@@ -18,7 +18,7 @@ export async function extractPackageFile(
 ): Promise<PackageFileContent | null> {
   logger.trace(`poetry.extractPackageFile(${packageFile})`);
   const { val: res, err } = Result.parse(
-    stripTemplates(content),
+    massageToml(content),
     PoetrySchemaToml.transform(({ packageFileContent }) => packageFileContent),
   ).unwrap();
   if (err) {
diff --git a/lib/util/toml.spec.ts b/lib/util/toml.spec.ts
index b219c42b65..a589d36dd3 100644
--- a/lib/util/toml.spec.ts
+++ b/lib/util/toml.spec.ts
@@ -1,5 +1,5 @@
 import { codeBlock } from 'common-tags';
-import { parse as parseToml } from './toml';
+import { massage, parse as parseToml } from './toml';
 
 describe('util/toml', () => {
   it('works', () => {
@@ -28,4 +28,20 @@ describe('util/toml', () => {
 
     expect(() => parseToml(input)).toThrow(SyntaxError);
   });
+
+  it('handles templates', () => {
+    const input = codeBlock`
+      [tool.poetry]
+      name = "{{ name }}"
+      {# comment #}
+      [tool.poetry.dependencies]
+      python = "^3.9"
+      {{ foo }} = "{{ bar }}"
+      {% if foo %}
+      dep1 = "^1.0.0"
+      {% endif %}
+    `;
+
+    expect(() => parseToml(massage(input))).not.toThrow(SyntaxError);
+  });
 });
diff --git a/lib/util/toml.ts b/lib/util/toml.ts
index 51814334a5..dbee015afc 100644
--- a/lib/util/toml.ts
+++ b/lib/util/toml.ts
@@ -1,6 +1,12 @@
 import { getStaticTOMLValue, parseTOML } from 'toml-eslint-parser';
+import { regEx } from './regex';
+import { stripTemplates } from './string';
 
 export function parse(input: string): unknown {
   const ast = parseTOML(input);
   return getStaticTOMLValue(ast);
 }
+
+export function massage(input: string): string {
+  return stripTemplates(input.replace(regEx(/^\s*{{.+?}}\s*=.*$/gm), ''));
+}
-- 
GitLab