Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
acd46bb
Fix bug
okorach-sonar Oct 13, 2025
6924440
Convert exceptions at the root
okorach-sonar Oct 14, 2025
2f821b5
Adjust to exceptions in platform
okorach-sonar Oct 14, 2025
3ea1d6f
Make gitlab groups a list setting
okorach-sonar Oct 14, 2025
5a643ab
Remove au lowercasing for API errors
okorach-sonar Oct 14, 2025
2dcba71
Adjust to exceptions in platform
okorach-sonar Oct 14, 2025
849b5a9
Allow new param as multi-valued
okorach-sonar Oct 14, 2025
064428b
Adjust to QG not found message
okorach-sonar Oct 14, 2025
1b573ce
Separate exception for HTTP and Connection Error
okorach-sonar Oct 15, 2025
e587272
Simplify import_zip excepton handling
okorach-sonar Oct 15, 2025
b256598
Disallowed to remove admin permission to the admin user
okorach-sonar Oct 15, 2025
342e828
Adjust exception handling
okorach-sonar Oct 15, 2025
4961fd6
Add illegal issue transition
okorach-sonar Oct 15, 2025
2172133
Add illegal issue transition
okorach-sonar Oct 15, 2025
aba3b08
Fix
okorach-sonar Oct 15, 2025
55d7b4a
Raise IllegalIssueTransition
okorach-sonar Oct 15, 2025
c919335
Remove Illegal Issue Transition Exception
okorach-sonar Oct 15, 2025
8afc5f1
Remove illegal issue err code
okorach-sonar Oct 15, 2025
3d114b2
Remove nolint option from the run_scanner call
okorach-sonar Oct 15, 2025
5bb22dc
Formatting
okorach-sonar Oct 15, 2025
38ec67a
Fix key callection for object not found
okorach-sonar Oct 15, 2025
a03a3b4
Handle ObjectNotFound for import_zip
okorach-sonar Oct 15, 2025
763592e
Remove exception handling in delete()
okorach-sonar Oct 15, 2025
4110d77
Fixes on WebHook corner cases
okorach-sonar Oct 15, 2025
d4d6671
Add auto gen of unsupported operation for bad parameter value
okorach-sonar Oct 16, 2025
dec55ad
Remove ConnectionError, it's a type of RequestException
okorach-sonar Oct 16, 2025
61b7315
Remove useless CLI parameters process
okorach-sonar Oct 16, 2025
60bc76c
Raise UnsupportedOperationw hen passing a wrong severity
okorach-sonar Oct 16, 2025
c458701
Verify exception when passing incorrect parameters
okorach-sonar Oct 16, 2025
d31c6af
Verify exception when import non existing project key
okorach-sonar Oct 16, 2025
4610e4d
Verify exception when webhook with too small secret string
okorach-sonar Oct 16, 2025
b34bce8
Handle update when reference portfolio already exists
okorach-sonar Oct 16, 2025
0b41a84
Remove exception handling (handled in Platform)
okorach-sonar Oct 16, 2025
ba949c4
Add exception handling for incorretc API param value
okorach-sonar Oct 16, 2025
6a395ac
Quality pass
okorach-sonar Oct 16, 2025
769631d
Add type hints
okorach-sonar Oct 16, 2025
d4cec15
Quality pass
okorach-sonar Oct 16, 2025
3b066d3
Quality pass
okorach-sonar Oct 16, 2025
01b91c2
Fix key retrieval in case of exception
okorach-sonar Oct 16, 2025
5f69cca
Add message to detect not found
okorach-sonar Oct 16, 2025
3dd814d
Refactoring
okorach-sonar Oct 16, 2025
ce9e1e6
Adjust to 9.9
okorach-sonar Oct 16, 2025
6d82646
Adjust to 9.9
okorach-sonar Oct 16, 2025
aa697c2
Use poetry to install dependencies
okorach-sonar Oct 16, 2025
d5e928f
Fix run_linters
okorach-sonar Oct 16, 2025
2899b4e
Find ruff from python
okorach-sonar Oct 16, 2025
24d472b
Remove python to ruff ruff
okorach-sonar Oct 16, 2025
ad7e423
Install deps before running linters
okorach-sonar Oct 16, 2025
8e9ef2e
Remove install before runninng linters
okorach-sonar Oct 16, 2025
7f3dfc8
Try to run linters in same env
okorach-sonar Oct 17, 2025
69d85ce
Try again with venv
okorach-sonar Oct 17, 2025
a001cf1
Run linters in same step as install deps
okorach-sonar Oct 17, 2025
1e08bf6
remove venv
okorach-sonar Oct 17, 2025
3d852fe
Try again
okorach-sonar Oct 17, 2025
39c03c4
Fix
okorach-sonar Oct 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
if [ -f requirements-to-build.txt ]; then pip install -r requirements-to-build.txt; fi
python -m pip install poetry
poetry install
- name: Build package
run: |
poetry build
continue-on-error: false
# Linting is done in the run_linters.sh script

- name: Prep tests
Expand All @@ -49,9 +53,10 @@ jobs:
- name: Run linters
working-directory: .
run: |
python -m pip install ruff pylint flake8
chmod +x conf/run_linters.sh
conf/run_linters.sh
#- name: Cache SonarQube packages
# - name: Cache SonarQube packages
# uses: actions/cache@v4
# with:
# path: ./.sonar
Expand Down
2 changes: 1 addition & 1 deletion conf/prep_all_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function create_fresh_project {

conf/run_linters.sh

create_fresh_project "${SYNC_PROJECT_KEY}" "${SONAR_HOST_URL_TEST:?}" "${SONAR_TOKEN_TEST_ADMIN_USER}" "${SONAR_TOKEN_TEST_ADMIN_ANALYSIS}" -nolint
create_fresh_project "${SYNC_PROJECT_KEY}" "${SONAR_HOST_URL_TEST:?}" "${SONAR_TOKEN_TEST_ADMIN_USER}" "${SONAR_TOKEN_TEST_ADMIN_ANALYSIS}"
create_fresh_project "${SYNC_PROJECT_KEY}" "${SONAR_HOST_URL_LATEST:?}" "${SONAR_TOKEN_LATEST_ADMIN_USER}" "${SONAR_TOKEN_LATEST_ADMIN_ANALYSIS}"
create_fresh_project "${SYNC_PROJECT_KEY}" "${SONAR_HOST_URL_CB:?}" "${SONAR_TOKEN_CB_ADMIN_USER}" "${SONAR_TOKEN_CB_ADMIN_ANALYSIS}"
create_fresh_project "${SYNC_PROJECT_KEY}" "${SONAR_HOST_URL_9:?}" "${SONAR_TOKEN_9_ADMIN_USER}" "${SONAR_TOKEN_9_ADMIN_ANALYSIS}"
Expand Down
2 changes: 1 addition & 1 deletion conf/run_linters.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ fi
if [[ "${localbuild}" = "true" ]]; then
if [[ "${linters_to_run}" == *"shellcheck"* ]]; then
echo "===> Running shellcheck"
shellcheck $(find "${ROOT_DIR}" . -name '*.sh') \
shellcheck "$(find "${ROOT_DIR}" . -name '*.sh')" \
-s bash -f json | jq | tee "${BUILD_DIR}/shellcheck-report.json" | "${CONF_DIR}"/shellcheck2sonar.py "${external_format}" > "${SHELLCHECK_REPORT}"
[[ ! -s "${SHELLCHECK_REPORT}" ]] && rm -f "${SHELLCHECK_REPORT}"
cat "${BUILD_DIR}/shellcheck-report.json"
Expand Down
6 changes: 0 additions & 6 deletions migration/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ while [[ $# -ne 0 ]]; do
nodocker)
build_image=0
;;
pypi)
release=1
;;
dockerhub)
release_docker=1
;;
*)
;;
esac
Expand Down
2 changes: 1 addition & 1 deletion sonar/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def get_issues(self, filters: types.ApiParams = None) -> dict[str, object]:
"""Returns list of issues for a component, optionally on branches or/and PRs"""
from sonar.issues import search_all

filters = {k: list(set(v) if isinstance(v, (list, set, tuple)) else v) for k, v in (filters or {}).items() if v is not None}
filters = {k: list(set(v)) if isinstance(v, (list, set, tuple)) else v for k, v in (filters or {}).items() if v is not None}
log.info("Searching issues for %s with filters %s", str(self), str(filters))
issue_list = search_all(endpoint=self.endpoint, params=self.api_params() | {"additionalFields": "comments"} | filters)
self.nbr_issues = len(issue_list)
Expand Down
6 changes: 6 additions & 0 deletions sonar/findings.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import concurrent.futures
from datetime import datetime
from typing import Optional
import re
from http import HTTPStatus
from requests import RequestException
import Levenshtein
Expand All @@ -32,6 +33,7 @@
import sonar.platform as pf
from sonar.util import types
from sonar.util import constants as c, issue_defs as idefs
from sonar import exceptions

import sonar.utilities as util
from sonar import projects, rules
Expand Down Expand Up @@ -460,6 +462,10 @@ def do_transition(self, transition: str) -> bool:
return self.post("issues/do_transition", {"issue": self.key, "transition": transition}).ok
except (ConnectionError, RequestException) as e:
util.handle_error(e, f"applying transition {transition}", catch_http_statuses=(HTTPStatus.BAD_REQUEST, HTTPStatus.NOT_FOUND))
except exceptions.SonarException as e:
if re.match(r"Transition from state [A-Za-z]+ does not exist", e.message):
raise exceptions.UnsupportedOperation(e.message) from e
raise
return False

def get_branch_and_pr(self, data: types.ApiPayload) -> tuple[Optional[str], Optional[str]]:
Expand Down
10 changes: 3 additions & 7 deletions sonar/issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,18 +294,14 @@ def add_comment(self, comment: str) -> bool:
log.debug("Adding comment '%s' to %s", comment, str(self))
try:
r = self.post("issues/add_comment", {"issue": self.key, "text": comment})
except (ConnectionError, requests.RequestException) as e:
except requests.RequestException as e:
util.handle_error(e, "adding comment", catch_all=True)
return False
return r.ok

def __set_severity(self, **params) -> bool:
try:
log.debug("Changing severity of %s from '%s' to '%s'", str(self), self.severity, str(params))
r = self.post("issues/set_severity", {"issue": self.key, **params})
except (ConnectionError, requests.RequestException) as e:
util.handle_error(e, "changing issue severity", catch_all=True)
return False
log.debug("Changing severity of %s from '%s' to '%s'", str(self), self.severity, str(params))
r = self.post("issues/set_severity", {"issue": self.key, **params})
return r.ok

def set_severity(self, severity: str) -> bool:
Expand Down
3 changes: 3 additions & 0 deletions sonar/permissions/project_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ def _set_perms(
self.read()
for p in permissions.PERMISSION_TYPES:
to_remove = diff_func(self.permissions.get(p, {}), new_perms.get(p, {}))
if p == "users" and "admin" in to_remove:
# Don't remove admin permission to the admin user, this is not possible anyway
to_remove["admin"] = [v for v in to_remove["admin"] if v != "admin"]
self._post_api(apis["remove"][p], field[p], to_remove, **kwargs)
to_add = diff_func(new_perms.get(p, {}), self.permissions.get(p, {}))
self._post_api(apis["add"][p], field[p], to_add, **kwargs)
Expand Down
24 changes: 19 additions & 5 deletions sonar/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from http import HTTPStatus
import sys
import os
import re
from typing import Optional
import time
import datetime
Expand Down Expand Up @@ -264,9 +265,8 @@ def __run_request(self, request: callable, api: str, params: types.ApiParams = N
headers["Authorization"] = f"Bearer {self.__token}"
if with_org:
params["organization"] = self.organization
req_type, url = "", ""
req_type, url = getattr(request, "__name__", repr(request)).upper(), ""
if log.get_level() <= log.DEBUG:
req_type = getattr(request, "__name__", repr(request)).upper()
url = self.__urlstring(api, params, kwargs.get("data", {}))
log.debug("%s: %s", req_type, url)
kwargs["headers"] = headers
Expand All @@ -288,10 +288,24 @@ def __run_request(self, request: callable, api: str, params: types.ApiParams = N
self.local_url = new_url
r.raise_for_status()
except HTTPError as e:
lvl = log.DEBUG if r.status_code in mute else log.ERROR
code = r.status_code
lvl = log.DEBUG if code in mute else log.ERROR
log.log(lvl, "%s (%s request)", util.error_msg(e), req_type)
raise e
except (ConnectionError, RequestException) as e:
err_msg = util.sonar_error(e.response)
err_msg_lower = err_msg.lower()
key = next((params[k] for k in ("key", "project", "component", "componentKey") if k in params), "Unknown")
if any(
msg in err_msg_lower for msg in ("not found", "no quality gate has been found", "does not exist", "could not find")
): # code == HTTPStatus.NOT_FOUND:
raise exceptions.ObjectNotFound(key, err_msg) from e
if any(msg in err_msg_lower for msg in ("already exists", "already been taken")):
raise exceptions.ObjectAlreadyExists(key, err_msg) from e
if re.match(r"(Value of parameter .+ must be one of|No enum constant)", err_msg):
raise exceptions.UnsupportedOperation(err_msg) from e
if any(msg in err_msg_lower for msg in ("insufficient privileges", "insufficient permissions")):
raise exceptions.SonarException(err_msg, errcodes.SONAR_API_AUTHORIZATION) from e
raise exceptions.SonarException(err_msg, errcodes.SONAR_API) from e
except ConnectionError as e:
util.handle_error(e, "")
return r

Expand Down
9 changes: 7 additions & 2 deletions sonar/portfolios.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ def add_application_branch(self, app_key: str, branch: str = c.DEFAULT_BRANCH) -
self._applications[app_key].append(branch)
return True

def add_subportfolio(self, key: str, name: str = None, by_ref: bool = False) -> object:
def add_subportfolio(self, key: str, name: str = None, by_ref: bool = False) -> Portfolio:
"""Adds a subportfolio to a portfolio, defined by key, name and by reference option"""

log.info("Adding sub-portfolios to %s", str(self))
Expand Down Expand Up @@ -644,7 +644,12 @@ def update(self, data: dict[str, str], recurse: bool) -> None:
if subp_data.get("byReference", False):
o_subp = Portfolio.get_object(self.endpoint, key)
if o_subp.key not in key_list:
self.add_subportfolio(o_subp.key, name=o_subp.name, by_ref=True)
try:
self.add_subportfolio(o_subp.key, name=o_subp.name, by_ref=True)
except exceptions.SonarException as e:
# If the exception is that the portfolio already references, just pass
if "already references" not in e.message:
raise
else:
try:
o_subp = Portfolio.get_object(self.endpoint, key)
Expand Down
Loading