Python Code Quality: GitLab, SonarQube, OWASP, Dependency-Check, and Essential Dev Tools Integration
Overview
Purpose
In the fast-evolving landscape of software development, ensuring code quality and security is paramount. To streamline this process, integrating a robust set of tools becomes imperative. In this blog, we embark on a journey to seamlessly integrate GitLab, SonarQube, pytest, coverage.py, and dependency-check into a unified ecosystem. By combining version control, code analysis, testing, code coverage evaluation, and dependency scanning, we aim to create a powerful and efficient pipeline that not only enhances the overall quality of the codebase but also fortifies it against potential vulnerabilities. Join us as we explore each tool's role in this integration, demonstrating how they collectively contribute to a more resilient and high-performing software development lifecycle.
Requirements
After the code is committed, these reports should be generated by CI/CD pipelines automatically.
- Test result and test coverage report by pytest and Coverage.py.
- Dependency check report by Dependency-Check
- Code quality by SonarQube
- Sommarize these reports into GitLab & SonarQube.
Procedures
Test result and coverage
Test result and coverage in console and HTML reports (Pytest and Coverage.py)
-
Test result (Console):
You can add
-rAR
in pyproject.toml to show test result on console. -
Test result (HTML):
I don't try it, you can try the following 2 tools. The 1st is provided by pytest but it seems simple. The 2nd seems beautifuler, but the last commit date is 2022/04/29.
-
Test coverage (Console):
You can add
--cov-report=term
(term means terminal) in pyproject.toml to show test coverage on console. -
Test coverage (HTML):
You can add
--cov-report=html
in pyproject.toml to generate test coverage HTML report.
Test Result and coverage on GitLab
-
If you want to show test results in GitLab, you need to add
--junitxml=junit_report.xml
(GitLab supports JUnit format) and--cov-report=xml
(GitLab supports Cobertura format.) inpyproject.toml
to ask pytest to generate corresponding xml files.1[tool.pytest.ini_options] 2python_files = ["tests/*.py"] 3addopts = "-R --cov=. --cov-report=term --cov-report=xml --cov-report=html --junitxml=junit_report.xml"
-
Then modify
.gitlab-ci.yml
to upload JUnit and Coverage XML files into GitLab artifacts.1artifacts: 2 reports: 3 junit: junit_report.xml 4 coverage_report: # coverage_report is supported in GitLab 14.10 5 coverage_format: cobertura 6 path: coverage.xml
-
When you run GitLab CI/CD pipelines, you can see the following log in
pytest
stage.1- generated xml file: /builds/-i39jfYQ/0/ai/service_test/junit_report.xml - 2... 3Uploading artifacts... 4junit_report.xml: found 1 matching artifact files and directories 5Uploading artifacts as "junit" to coordinator... 201 Created id=3169 responseStatus=201 Created token=********
-
After the GitLab CI/CD pipeline is completed, you can see the test summary.
-
And you can click the job to see the detail.
Test Result and coverage on SonarQube
SonarQube can show the test results in old version, but I'm not sure when SonarQube removed it. You can see test coverage only on SonarQube now.
-
You need to add
--cov-report=xml
in pyproject.toml to generatecoverage.xml
-
You need to add
-D"sonar.python.coverage.reportPaths=./coverage.xml"
in .gitlab-ci.yml to ask SonarQube to load coverage.xml into SonarQube. -
When you run GitLab CI/CD pipelines, you can see the following logs in
pytest
stage.1Coverage XML written to file coverage.xml
-
And you can see the coverage report in SonarQube now.
Dependency Check
-
There are several docker images, please use the first one, or it will download all CVE updates (from 2002 to now) every time. (reference: https://hub.docker.com/r/owasp/dependency-check-action/)
-
Because Python is still in experientmal, please add
--enableExperimental
parameter. -
Please add
-n
in the parameter to stop downloading updates, or you will see the following log, it takes several minutes to execute.1... 2[INFO] Checking for updates 3[INFO] NVD CVE requires several updates; this could take a couple of minutes. 4[INFO] Download Started for NVD CVE - 2002 5[INFO] Download Complete for NVD CVE - 2002 (1841 ms) 6[INFO] Processing Started for NVD CVE - 2002 7[INFO] Download Started for NVD CVE - 2003 8[INFO] Download Complete for NVD CVE - 2003 (1425 ms) 9[INFO] Processing Started for NVD CVE - 2003 10... 11[INFO] Download Started for NVD CVE - 2023 12[INFO] Download Complete for NVD CVE - 2023 (2710 ms) 13[INFO] Processing Started for NVD CVE - 2023 14... 15[INFO] Updated the CPE ecosystem on 136030 NVD records 16...
-
Reports (on GitLab)
Depends on your requirement, you can put dependency check result on GitLab CI/CD pipelines, HTML reports, or SonarQube.
-
If there is a security vulnerability, you can see it in GitLab CI/CD pipeline logs, for example, I use bootstrap 3.2 and it shows the error.
1[ERROR] 2One or more dependencies were identified with vulnerabilities that have a CVSS score greater than or equal to '0.0': 3bootstrap.min.js: Bootstrap before 4.0.0 is end-of-life and no longer maintained.(3.9) 4requirements.txt: CVE-2022-25882(7.5)
-
If you want to download HTML report, you can add
dependency-check-report.html
in artifacts of .gitlab-ci.yml.1artifacts: 2 when: always 3 paths: 4 - "./dependency-check-report.html"
-
-
Reports (on SonarQube)
-
You need to install Dependency-Check Plugin for SonarQube
-
Add
dependency-check-report.json
,sonar.dependencyCheck.jsonReportPath
, andsonar.dependencyCheck.htmlReportPath
in .gitlab-ci.yml.1artifacts: 2 when: always 3 paths: 4 - "./dependency-check-report.html" 5 - "./dependency-check-report.json"
1-D"sonar.dependencyCheck.jsonReportPath=../dependency-check-report.json" 2-D"sonar.dependencyCheck.htmlReportPath=../dependency-check-report.html"
-
The plugin will insert security vulnerabilities into SonarQube's result, you can see the detail in the issues.
-
If you want to see the HTML report in SonarQube, you can click the More tab and see it.
-
Code quality (SonarQube)
The dashboard in SonarQube 10.0 is a little different than the previous version.
-
SonarQube:
Configuration files
pyproject.toml
-
The following is the completed pyproject.toml
1[tool.pytest.ini_options] 2python_files = ["tests/*.py"] 3addopts = "-rAR --cov=. --cov-report=term --cov-report=xml --cov-report=html --junitxml=junit_report.xml" 4log_cli = true 5log_cli_level = "INFO" 6 7[tool.coverage.run] 8branch = true 9omit = [ 10 "*/tests/*", 11 "config.py", # it seems a bug, refer https://github.com/nedbat/coveragepy/issues/1653 12 "config-3.py", 13] 14 15[tool.coverage.paths] 16source = ["."] 17 18[tool.coverage.html] 19directory = "coverage_html_report"
.gitlab-ci.yml
-
The following is the completed .gitlab-ci.yml
1variables: 2 PROJECT_NAME: "Service_test" 3 VER: 1.0.0 4 DOCKER_FILE_NAME: service_test 5 SONARQUBE_URL: https://example.com:9000/ 6 SONARQUBE_TOKEN: squ_**************************************** 7 SONARQUBE_PROJECT_KEY: service_test 8 PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache.pip" 9 10image: python:3.11.6 11 12cache: 13 paths: 14 - .cache/pip 15 - venv/ 16 17stages: 18 - test 19 - dependency_check 20 - quality_check 21 22test: 23 stage: test 24 script: 25 # Show basic information 26 - python --version 27 - pip list 28 29 # Prepare virtual environment 30 - pip install --upgrade pip 31 - pip install virtualenv 32 - virtualenv venv 33 - source venv/bin/activate 34 35 # Install libraries 36 - pip install -r requirements.txt 37 - pip list 38 39 # Test 40 - pytest --version 41 - pytest 42 artifacts: 43 reports: 44 junit: junit_report.xml 45 coverage_report: # coverage_report is supported after GitLab 14.10 46 coverage_format: cobertura 47 path: coverage.xml 48 49 paths: 50 - coverage_html_report 51 - coverage.xml # Save coverage XML report as an artifact 52 53dependency_check: 54 stage: dependency_check 55 image: 56 #name: registry.gitlab.com/gitlab-ci-utils/docker-dependency-check:latest 57 name: owasp/dependency-check-action:latest 58 entrypoint: [""] 59 script: 60 - - /usr/share/dependency-check/bin/dependency-check.sh -n --scan "." --format ALL --project "$PROJECT_NAME" --failOnCVSS 0 --enableExperimental --log ./dependency-check.log 61 artifacts: 62 when: always 63 paths: 64 - "./dependency-check-report.html" 65 - "./dependency-check-report.json" 66 - "./dependency-check.log" 67 68quality_check: 69 stage: quality_check 70 image: 71 name: sonarsource/sonar-scanner-cli:5.0.1 72 script: 73 - echo $PROJECT_NAME 74 - echo $SONARQUBE_URL 75 - echo $SONARQUBE_TOKEN 76 - sonar-scanner -D"sonar.host.url=$SONARQUBE_URL" 77 -D"sonar.token=$SONARQUBE_TOKEN" 78 -D"sonar.projectKey=$SONARQUBE_PROJECT_KEY" 79 -D"sonar.projectName=$PROJECT_NAME" 80 -D"sonar.sourceEncoding=utf-8" 81 -D"sonar.exclusions=test/**/*, coverage/**/*, test-report/**/*, dependency-check-report.html" 82 -D"sonar.javascript.lcov.reportPaths=./coverage/lcov.info" 83 -D"sonar.python.coverage.reportPaths=./coverage.xml" 84 -D"sonar.dependencyCheck.jsonReportPath=../dependency-check-report.json" 85 -D"sonar.dependencyCheck.htmlReportPath=../dependency-check-report.html" 86 -D"sonar.python.version=3.8,3.11"
Posts in this Series
- GitLab Node.js SonarQube Integration
- Python Code Quality: GitLab, SonarQube, OWASP, Dependency-Check, and Essential Dev Tools Integration
- Run SonarQube on Linux Docker with Mono to scan .NET 4.8 Code
- GitLab SonarQube Integration with .NET
- Use Grafana to Manage SonarQube KPI
- Use Grafana to Manage GitLab CI/CD Pipelines
- Use GitLab to do .NET 4.8 CI/CD
- GitLab Checkmarx CI/CD Integration
- GitLab Checkmarx SonarQube Integration