Skip to content

Commit d830135

Browse files
MJ/SCANPY-237 Adjusted line length and updated CLI help text.
1 parent a5c57bc commit d830135

File tree

4 files changed

+48
-68
lines changed

4 files changed

+48
-68
lines changed

CLI_ARGS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848

4949
| Option | Description |
5050
| ------ | ----------- |
51+
| `--dry-run`, `--no-dry-run` | Enable dry-run mode to validate configuration without connecting to SonarQube server or submitting analysis. See DRY_RUN_MODE.md for details |
5152
| `--skip-jre-provisioning`, `-Dsonar.scanner.skipJreProvisioning` | If provided, the provisioning of the JRE will be skipped |
5253
| `--sonar-branch-name`, `-Dsonar.branch.name` | Name of the branch being analyzed |
5354
| `--sonar-build-string`, `-Dsonar.buildString` | The string passed with this property will be stored with the analysis and available in the results of api/project_analyses/search, thus allowing you to later identify a specific analysis and obtain its key for use with api/new_code_periods/set on the SPECIFIC_ANALYSIS type |
@@ -73,7 +74,6 @@
7374
| `--sonar-scanner-arch`, `-Dsonar.scanner.arch` | Architecture on which the scanner will be running |
7475
| `--sonar-scanner-cloud-url`, `-Dsonar.scanner.cloudUrl` | SonarQube Cloud base URL, https://sonarcloud.io for example |
7576
| `--sonar-scanner-connect-timeout`, `-Dsonar.scanner.connectTimeout` | Time period to establish connections with the server (in seconds) |
76-
| `--dry-run`, `--no-dry-run` | Enable dry-run mode to validate configuration without connecting to SonarQube server or submitting analysis. See [Dry Run Mode](DRY_RUN_MODE.md) for details. Can also be set via `-Dsonar.scanner.dryRun=true` or `SONAR_SCANNER_DRY_RUN=true` |
7777
| `--sonar-scanner-internal-dump-to-file`, `-Dsonar.scanner.internal.dumpToFile` | Filename where the input to the scanner engine will be dumped. Useful for debugging |
7878
| `--sonar-scanner-internal-sq-version`, `-Dsonar.scanner.internal.sqVersion` | Emulate the result of the call to get SQ server version. Useful for debugging with --sonar-scanner-internal-dump-to-file |
7979
| `--sonar-scanner-java-exe-path`, `-Dsonar.scanner.javaExePath` | If defined, the scanner engine will be run with this JRE |

src/pysonar_scanner/configuration/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ def __create_parser(cls):
367367
"--dry-run",
368368
action=argparse.BooleanOptionalAction,
369369
default=None,
370-
help="Enable dry-run mode to validate configuration without connecting to SonarQube server or submitting analysis",
370+
help="Enable dry-run mode to validate configuration without connecting to SonarQube server or submitting analysis. See DRY_RUN_MODE.md for details",
371371
)
372372

373373
jvm_group = parser.add_argument_group("JVM Settings")

src/pysonar_scanner/dry_run_reporter.py

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -50,24 +50,36 @@ def report_configuration(config: dict[str, Any]) -> None:
5050
logging.info("DRY RUN MODE - Configuration Report")
5151
logging.info("=" * 80)
5252

53-
DryRunReporter._log_section("Project Configuration", {
54-
SONAR_PROJECT_KEY: config.get(SONAR_PROJECT_KEY),
55-
SONAR_PROJECT_NAME: config.get(SONAR_PROJECT_NAME),
56-
SONAR_ORGANIZATION: config.get(SONAR_ORGANIZATION, "N/A (likely SonarQube Server)"),
57-
})
58-
59-
DryRunReporter._log_section("Server Configuration", {
60-
SONAR_HOST_URL: config.get(SONAR_HOST_URL, "N/A"),
61-
})
62-
63-
DryRunReporter._log_section("Source Configuration", {
64-
SONAR_SOURCES: config.get(SONAR_SOURCES, "N/A"),
65-
SONAR_TESTS: config.get(SONAR_TESTS, "N/A"),
66-
})
67-
68-
DryRunReporter._log_section("Coverage Configuration", {
69-
SONAR_PYTHON_COVERAGE_REPORT_PATHS: config.get(SONAR_PYTHON_COVERAGE_REPORT_PATHS, "N/A"),
70-
})
53+
DryRunReporter._log_section(
54+
"Project Configuration",
55+
{
56+
SONAR_PROJECT_KEY: config.get(SONAR_PROJECT_KEY),
57+
SONAR_PROJECT_NAME: config.get(SONAR_PROJECT_NAME),
58+
SONAR_ORGANIZATION: config.get(SONAR_ORGANIZATION, "N/A (likely SonarQube Server)"),
59+
},
60+
)
61+
62+
DryRunReporter._log_section(
63+
"Server Configuration",
64+
{
65+
SONAR_HOST_URL: config.get(SONAR_HOST_URL, "N/A"),
66+
},
67+
)
68+
69+
DryRunReporter._log_section(
70+
"Source Configuration",
71+
{
72+
SONAR_SOURCES: config.get(SONAR_SOURCES, "N/A"),
73+
SONAR_TESTS: config.get(SONAR_TESTS, "N/A"),
74+
},
75+
)
76+
77+
DryRunReporter._log_section(
78+
"Coverage Configuration",
79+
{
80+
SONAR_PYTHON_COVERAGE_REPORT_PATHS: config.get(SONAR_PYTHON_COVERAGE_REPORT_PATHS, "N/A"),
81+
},
82+
)
7183

7284
@staticmethod
7385
def report_validation_results(validation_result: "ValidationResult") -> int:
@@ -160,28 +172,20 @@ def validate_coverage_reports(
160172
report_paths = [p.strip() for p in coverage_paths.split(",")]
161173

162174
for report_path in report_paths:
163-
CoverageReportValidator._validate_single_report(
164-
report_path, base_path, validation_result
165-
)
175+
CoverageReportValidator._validate_single_report(report_path, base_path, validation_result)
166176

167177
@staticmethod
168-
def _validate_single_report(
169-
report_path: str, base_path: Path, validation_result: ValidationResult
170-
) -> None:
178+
def _validate_single_report(report_path: str, base_path: Path, validation_result: ValidationResult) -> None:
171179
"""Validate a single coverage report file."""
172180
# Resolve relative path
173181
full_path = base_path / report_path if not Path(report_path).is_absolute() else Path(report_path)
174182

175183
if not full_path.exists():
176-
validation_result.add_error(
177-
f"Coverage report not found: {report_path} (resolved to {full_path})"
178-
)
184+
validation_result.add_error(f"Coverage report not found: {report_path} (resolved to {full_path})")
179185
return
180186

181187
if not full_path.is_file():
182-
validation_result.add_error(
183-
f"Coverage report is not a file: {report_path} (resolved to {full_path})"
184-
)
188+
validation_result.add_error(f"Coverage report is not a file: {report_path} (resolved to {full_path})")
185189
return
186190

187191
try:
@@ -195,20 +199,16 @@ def _validate_single_report(
195199
else:
196200
logging.info(f" ✓ Coverage report is valid Cobertura XML: {report_path}")
197201
except PermissionError:
198-
validation_result.add_error(
199-
f"Coverage report is not readable (permission denied): {report_path}"
200-
)
202+
validation_result.add_error(f"Coverage report is not readable (permission denied): {report_path}")
201203
except UnicodeDecodeError:
202204
validation_result.add_warning(
203205
f"Coverage report may not be text-based (is it in binary format?): {report_path}"
204206
)
205207
except ET.ParseError as e:
206208
validation_result.add_error(
207-
f"Coverage report is not valid XML (Cobertura format): {report_path}\n"
208-
f" Parse error: {str(e)}"
209+
f"Coverage report is not valid XML (Cobertura format): {report_path}\n" f" Parse error: {str(e)}"
209210
)
210211
except Exception as e:
211212
validation_result.add_error(
212-
f"Error validating coverage report format: {report_path}\n"
213-
f" Error: {str(e)}"
213+
f"Error validating coverage report format: {report_path}\n" f" Error: {str(e)}"
214214
)

tests/unit/test_dry_run.py

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,7 @@ def test_validate_single_report_file_not_found(self):
156156
self.fs.create_dir("/project")
157157
result = ValidationResult()
158158

159-
CoverageReportValidator.validate_coverage_reports(
160-
"coverage.xml", "/project", result
161-
)
159+
CoverageReportValidator.validate_coverage_reports("coverage.xml", "/project", result)
162160

163161
assert not result.is_valid()
164162
assert len(result.errors) == 1
@@ -170,9 +168,7 @@ def test_validate_single_report_valid_cobertura(self, mock_logging):
170168
self.fs.create_file("/project/coverage.xml", contents='<?xml version="1.0"?>\n<coverage></coverage>')
171169
result = ValidationResult()
172170

173-
CoverageReportValidator.validate_coverage_reports(
174-
"coverage.xml", "/project", result
175-
)
171+
CoverageReportValidator.validate_coverage_reports("coverage.xml", "/project", result)
176172

177173
assert result.is_valid()
178174
assert len(result.warnings) == 0
@@ -186,9 +182,7 @@ def test_validate_multiple_coverage_reports(self):
186182
self.fs.create_file("/project/coverage2.xml", contents='<?xml version="1.0"?>\n<coverage></coverage>')
187183
result = ValidationResult()
188184

189-
CoverageReportValidator.validate_coverage_reports(
190-
"coverage1.xml, coverage2.xml", "/project", result
191-
)
185+
CoverageReportValidator.validate_coverage_reports("coverage1.xml, coverage2.xml", "/project", result)
192186

193187
assert result.is_valid()
194188

@@ -197,9 +191,7 @@ def test_validate_report_not_a_file(self):
197191
self.fs.create_dir("/project/coverage.xml")
198192
result = ValidationResult()
199193

200-
CoverageReportValidator.validate_coverage_reports(
201-
"coverage.xml", "/project", result
202-
)
194+
CoverageReportValidator.validate_coverage_reports("coverage.xml", "/project", result)
203195

204196
assert not result.is_valid()
205197
assert "not a file" in result.errors[0]
@@ -209,24 +201,17 @@ def test_validate_report_invalid_xml(self):
209201
self.fs.create_file("/project/coverage.xml", contents="not valid xml")
210202
result = ValidationResult()
211203

212-
CoverageReportValidator.validate_coverage_reports(
213-
"coverage.xml", "/project", result
214-
)
204+
CoverageReportValidator.validate_coverage_reports("coverage.xml", "/project", result)
215205

216206
assert not result.is_valid()
217207
assert "not valid XML" in result.errors[0]
218208

219209
def test_validate_report_wrong_root_element(self):
220210
self.fs.create_dir("/project")
221-
self.fs.create_file(
222-
"/project/coverage.xml",
223-
contents='<?xml version="1.0"?>\n<report></report>'
224-
)
211+
self.fs.create_file("/project/coverage.xml", contents='<?xml version="1.0"?>\n<report></report>')
225212
result = ValidationResult()
226213

227-
CoverageReportValidator.validate_coverage_reports(
228-
"coverage.xml", "/project", result
229-
)
214+
CoverageReportValidator.validate_coverage_reports("coverage.xml", "/project", result)
230215

231216
assert result.is_valid()
232217
assert len(result.warnings) == 1
@@ -238,9 +223,7 @@ def test_validate_mixed_valid_and_missing_reports(self):
238223
self.fs.create_file("/project/exists.xml", contents='<?xml version="1.0"?>\n<coverage></coverage>')
239224
result = ValidationResult()
240225

241-
CoverageReportValidator.validate_coverage_reports(
242-
"exists.xml, missing.xml", "/project", result
243-
)
226+
CoverageReportValidator.validate_coverage_reports("exists.xml, missing.xml", "/project", result)
244227

245228
assert not result.is_valid()
246229
assert len(result.errors) == 1
@@ -267,10 +250,7 @@ def test_run_dry_run_no_coverage_reports(self, mock_logging):
267250
@patch("pysonar_scanner.__main__.logging")
268251
def test_run_dry_run_with_valid_coverage_reports(self, mock_logging):
269252
self.fs.create_dir("/project")
270-
self.fs.create_file(
271-
"/project/coverage.xml",
272-
contents='<?xml version="1.0"?>\n<coverage></coverage>'
273-
)
253+
self.fs.create_file("/project/coverage.xml", contents='<?xml version="1.0"?>\n<coverage></coverage>')
274254
config = {
275255
SONAR_PROJECT_KEY: "my-project",
276256
SONAR_PROJECT_BASE_DIR: "/project",

0 commit comments

Comments
 (0)