Skip to content

Commit

Permalink
PYSCAN-43 Ensure CLI arguments to the sonar-scanner-cli are provided …
Browse files Browse the repository at this point in the history
…intentionally
  • Loading branch information
guillaume-dequenne-sonarsource committed Jan 19, 2024
1 parent 7acba3d commit 3999baf
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 22 deletions.
3 changes: 3 additions & 0 deletions its/sources/minimal/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
[project]
name = "minimal"

[tool.poetry]
version = "1.5"

[tool.sonar]
projectKey="minimal"
sources="src"
Expand Down
1 change: 1 addition & 0 deletions its/sources/minimal/sonar-project.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sonar.projectVersion=1.2
20 changes: 20 additions & 0 deletions its/tests/test_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,23 @@ def test_minimal_project(sonarqube_client: SonarQubeClient):
response = sonarqube_client.get_project_issues("minimal")
data = response.json()
assert len(data["issues"]) == 1
analyses_data = sonarqube_client.get_project_analyses("minimal").json()
latest_analysis_data = analyses_data['analyses'][0]
assert latest_analysis_data['projectVersion'] == '1.2'


def test_minimal_project_read_poetry_data(sonarqube_client: SonarQubeClient):
process = CliClient().run_analysis(sonarqube_client, params=["-read.project.config"], sources_dir="minimal")
assert process.returncode == 0
response = sonarqube_client.get_project_issues("minimal")
data = response.json()
assert len(data["issues"]) == 1
analyses_data = sonarqube_client.get_project_analyses("minimal").json()
latest_analysis_data = analyses_data['analyses'][0]
assert latest_analysis_data['projectVersion'] == '1.5'


def test_minimal_project_unexpected_arg(sonarqube_client: SonarQubeClient):
process = CliClient().run_analysis(sonarqube_client, params=["-unexpected"], sources_dir="minimal")
assert process.returncode == 0
assert "ERROR: Unrecognized option: -unexpected" in process.stdout
3 changes: 3 additions & 0 deletions its/utils/sonarqube_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ def wait_for_analysis_completion(self):

def get_project_issues(self, project_key: str):
return self.session.get(f"{self.base_url}/api/issues/search?projects={project_key}")

def get_project_analyses(self, project_key: str):
return self.session.get(f"{self.base_url}/api/project_analyses/search?project={project_key}")
16 changes: 13 additions & 3 deletions src/py_sonar_scanner/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,25 @@ def __init__(self):
self.sonar_scanner_executable_path = ""
self.scan_arguments = []
self.wrapper_arguments = argparse.Namespace(debug=False, read_project_config=False)
self.unknown_arguments = []

def setup(self) -> None:
"""This is executed when run from the command line"""
self._read_wrapper_arguments()
ApplicationLogger.set_debug(self.is_debug())
self.scan_arguments = self._read_toml_args()
# The sonar-scanner-cli may crash with an error when receiving unexpected arguments
# Therefore, arguments only expected by the sonar-scanner-python are filtered out.
self._append_common_arguments()
# If duplicate properties are provided, newer values will override older ones.
# We therefore read CLI arguments last so that they have priority over toml configuration.
self.scan_arguments.extend(sys.argv[1:])
self.scan_arguments.extend(self.unknown_arguments)

def _append_common_arguments(self):
if self.wrapper_arguments.debug:
self.scan_arguments.append("-X")
if self.wrapper_arguments.project_home is not None:
self.scan_arguments.append(f"-Dproject.home={self.wrapper_arguments.project_home}")

def _read_wrapper_arguments(self):
argument_parser = argparse.ArgumentParser()
Expand All @@ -68,7 +78,7 @@ def _read_wrapper_arguments(self):
action="store_true",
dest="read_project_config",
)
self.wrapper_arguments, _ = argument_parser.parse_known_args(args=sys.argv[1:])
self.wrapper_arguments, self.unknown_arguments = argument_parser.parse_known_args(args=sys.argv[1:])

def _read_toml_args(self) -> list[str]:
scan_arguments: list[str] = []
Expand Down Expand Up @@ -107,7 +117,7 @@ def _extract_from_poetry_properties(self, poetry_properties):
if "name" in poetry_properties:
result["project.name"] = poetry_properties["name"]
if "version" in poetry_properties:
result["project.version"] = poetry_properties["version"]
result["projectVersion"] = poetry_properties["version"]
# Note: Python version can be extracted from dependencies.python, however it
# may be specified with constraints, e.g ">3.8", which is not currently supported by sonar-python
return result
Expand Down
24 changes: 5 additions & 19 deletions tests/test_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def test_argument_parsing(self, mock_sys):
configuration.setup()
self.assertListEqual(
configuration.scan_arguments,
["-Dsonar.a=b", "-Dsonar.c=d", f"-Dtoml.path={CURRENT_DIR}/resources/pyproject.toml"],
["-Dsonar.a=b", "-Dsonar.c=d"],
)

mock_sys.argv = [SAMPLE_SCANNER_PATH, f"-Dproject.home={CURRENT_DIR}/resources/"]
Expand Down Expand Up @@ -139,7 +139,6 @@ def test_toml_with_valid_values(self, mock_sys):
"-Dsonar.project.name=overridden_name",
"-Dsonar.python.version=3.10",
"-Dsonar.property_class.property1=value1",
f"-Dtoml.path={CURRENT_DIR}/resources/{TEST_TOML_FILE_POETRY}",
],
)

Expand All @@ -153,29 +152,22 @@ def test_toml_overridden_common_properties(self, mock_sys):
configuration.scan_arguments,
[
"-Dsonar.project.name=my_name",
"-Dsonar.project.version=0.0.1",
"-Dsonar.projectVersion=0.0.1",
"-Dsonar.project.name=overridden_name",
"-Dsonar.python.version=3.10",
"-Dsonar.property_class.property1=value1",
f"-Dtoml.path={CURRENT_DIR}/resources/{TEST_TOML_FILE_POETRY}",
"-read.project.config",
],
)

@patch("py_sonar_scanner.configuration.sys")
def test_toml_no_common_properties(self, mock_sys):
configuration = Configuration()
toml_file_path = os.path.join(CURRENT_DIR, "resources", TOML_NO_COMMON_PROPERTIES)
mock_sys.argv = [SAMPLE_SCANNER_PATH, f"-Dtoml.path={toml_file_path}", "-read.project.config"]
mock_sys.argv = [SAMPLE_SCANNER_PATH, f"-Dtoml.path={toml_file_path}", "-read.project.config", "-DsomeProp"]
configuration.setup()
self.assertListEqual(
configuration.scan_arguments,
[
"-Dsonar.project.name=my_project_name",
"-Dsonar.python.version=3.10",
f"-Dtoml.path={CURRENT_DIR}/resources/{TOML_NO_COMMON_PROPERTIES}",
"-read.project.config",
],
["-Dsonar.project.name=my_project_name", "-Dsonar.python.version=3.10", "-DsomeProp"],
)

@patch("py_sonar_scanner.configuration.sys")
Expand All @@ -190,7 +182,6 @@ def test_duplicate_values_toml_cli(self, mock_sys):
"-Dsonar.project.name=overridden_name",
"-Dsonar.python.version=3.10",
"-Dsonar.property_class.property1=value1",
f"-Dtoml.path={CURRENT_DIR}/resources/{TEST_TOML_FILE_POETRY}",
"-Dsonar.project.name=second_override",
],
)
Expand All @@ -206,12 +197,7 @@ def test_error_while_reading_toml_file(self, mock_sys, mock_open):
configuration = Configuration()
configuration.setup()

self.assertListEqual(
configuration.scan_arguments,
[
f"-Dtoml.path={CURRENT_DIR}/resources/{TEST_TOML_FILE_POETRY}",
],
)
self.assertListEqual(configuration.scan_arguments, [])

self.assertEqual(
"Error while opening .toml file: Test error while opening file.", log.records[0].getMessage()
Expand Down

0 comments on commit 3999baf

Please sign in to comment.