Skip to content

Commit 5a4e448

Browse files
SCANPY-116 Parse the scanner properties from pyproject.toml files
1 parent e98d1d2 commit 5a4e448

File tree

9 files changed

+375
-109
lines changed

9 files changed

+375
-109
lines changed

poetry.lock

Lines changed: 7 additions & 105 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ relative_files = true
2929

3030
[tool.poetry.dependencies]
3131
python = '>=3.9'
32-
toml = '>=0.10.2'
32+
tomli = '^2.2.1'
3333
requests = "^2.32.3"
3434
responses = "^0.25.6"
3535
pyfakefs = "^5.7.4"

src/pysonar_scanner/configuration/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from pysonar_scanner.configuration import properties
2323
from pysonar_scanner.configuration.cli import CliConfigurationLoader
2424
from pysonar_scanner.configuration.properties import SONAR_TOKEN, SONAR_PROJECT_BASE_DIR, Key
25-
from pysonar_scanner.configuration import properties, sonar_project_properties
25+
from pysonar_scanner.configuration import properties, sonar_project_properties, toml_file
2626

2727
from pysonar_scanner.exceptions import MissingKeyException
2828

@@ -43,6 +43,11 @@ def load() -> dict[Key, any]:
4343
# but we need to resolve them first to load the properties file
4444
base_dir = Path(cli_properties.get(SONAR_PROJECT_BASE_DIR, "."))
4545
resolved_properties.update(sonar_project_properties.load(base_dir))
46+
47+
toml_path_property = cli_properties.get("toml-path", ".")
48+
toml_dir = Path(toml_path_property) if "toml-path" in cli_properties else base_dir
49+
resolved_properties.update(toml_file.load(toml_dir))
50+
4651
resolved_properties.update(cli_properties)
4752
return resolved_properties
4853

src/pysonar_scanner/configuration/cli.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,10 @@ def __parse_cli_args(cls) -> argparse.Namespace:
207207
help="Directory containing the project to be analyzed. Default is the current directory",
208208
)
209209

210+
parser.add_argument(
211+
"--toml-path",
212+
type=str,
213+
help="Path to the pyproject.toml file. If not provided, it will look in the SONAR_PROJECT_BASE_DIR",
214+
)
215+
210216
return parser.parse_args()

src/pysonar_scanner/configuration/properties.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@
6161
SONAR_EXCLUSIONS: Key = "sonar.exclusions"
6262
SONAR_TESTS: Key = "sonar.tests"
6363

64+
# pysonar scanner specific properties
65+
TOML_PATH: Key = "toml-path"
66+
6467

6568
@dataclass
6669
class Property:
@@ -73,6 +76,15 @@ class Property:
7376
cli_getter: Optional[Callable[[argparse.Namespace], any]] = None
7477
"""function to get the value from the CLI arguments namespace. If None, the property is not settable via CLI"""
7578

79+
def python_name(self) -> str:
80+
"""Convert Java-style camel case name to Python-style kebab-case name."""
81+
result = []
82+
for i, char in enumerate(self.name):
83+
if char.isupper() and i > 0:
84+
result.append("-")
85+
result.append(char.lower())
86+
return "".join(result)
87+
7688

7789
# fmt: off
7890
PROPERTIES: list[Property] = [
@@ -123,7 +135,7 @@ class Property:
123135
),
124136
Property(
125137
name=SONAR_SCANNER_API_BASE_URL,
126-
default_value=None,
138+
default_value=None,
127139
cli_getter=lambda args: args.sonar_scanner_api_url
128140
),
129141
Property(
@@ -241,5 +253,10 @@ class Property:
241253
default_value=None,
242254
cli_getter=lambda args: args.sonar_project_name
243255
),
256+
Property(
257+
name=TOML_PATH,
258+
default_value=None,
259+
cli_getter=lambda args: args.toml_path
260+
),
244261
]
245262
# fmt: on
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#
2+
# Sonar Scanner Python
3+
# Copyright (C) 2011-2024 SonarSource SA.
4+
# mailto:info AT sonarsource DOT com
5+
#
6+
# This program is free software; you can redistribute it and/or
7+
# modify it under the terms of the GNU Lesser General Public
8+
# License as published by the Free Software Foundation; either
9+
# version 3 of the License, or (at your option) any later version.
10+
# This program is distributed in the hope that it will be useful,
11+
#
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
# Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public License
17+
# along with this program; if not, write to the Free Software Foundation,
18+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
#
20+
from pathlib import Path
21+
from typing import Dict
22+
import os
23+
import tomli
24+
25+
from pysonar_scanner.configuration import properties
26+
27+
28+
def flatten_config_dict(config: dict[str, any], prefix: str) -> dict[str, any]:
29+
"""Flatten nested dictionaries into dot notation keys"""
30+
result = {}
31+
for key, value in config.items():
32+
if isinstance(value, dict):
33+
result.update(flatten_config_dict(value, f"{prefix}{key}."))
34+
else:
35+
property_name = f"{prefix}{key}"
36+
result[property_name] = value
37+
return result
38+
39+
40+
def load(base_dir: Path) -> Dict[str, str]:
41+
filepath = base_dir / "pyproject.toml"
42+
if not os.path.isfile(filepath):
43+
return {}
44+
45+
try:
46+
with open(filepath, "rb") as f:
47+
toml_dict = tomli.load(f)
48+
49+
# Look for configuration in the tool.sonar section
50+
if "tool" in toml_dict and "sonar" in toml_dict["tool"]:
51+
sonar_config = toml_dict["tool"]["sonar"]
52+
python_to_java_names = {prop.python_name(): prop.name for prop in properties.PROPERTIES}
53+
flattened_sonar_config = flatten_config_dict(sonar_config, prefix="sonar.")
54+
return {python_to_java_names.get(key, key): value for key, value in flattened_sonar_config.items()}
55+
return {}
56+
except Exception:
57+
# If there's any error parsing the TOML file, return empty dict
58+
# SCANPY-135: We should log the pyproject.toml parsing error
59+
return {}

0 commit comments

Comments
 (0)