Sonar Scanner uses the wrong link in pipeline output

Version

SonarQube: 9.9.0.65466 (Developer edition)
Docker: sonarsource/sonar-scanner-cli:4.8.0

Deployment:

We have sonarqube deployed in kubernetes behind an authenticated proxy, therefore, communication between sonarqube and gitlab actually happens using kubernetes service URLs that do not match the public-facing URLs which our company uses when accessing the web UIs.

Issue

The GitLab pipeline that runs the scanner outputs a link to the analysis at the end. This link appears to use the base URL matching the SONAR_HOST_URL environment variable, rather than the sonar.core.serverBaseURL of the SonarQube instance.

Notes

We set the SONAR_HOST_URL in the GitLab pipeline to http://sonarqube.sonarqube.svc:9000 and as a result, that is the URL used in the scanner output when telling where to find the analysis, however, the MR decoration uses the server’s base URL. I would expect the URL in both locations to be the same. Glad it at least works in the MR decoration, however, when combined with this issue it makes it very confusing/difficult for our users to find the actual analysis URL.

Hello @Jacob_Williams thanks for reporting this issue.
I tried to reproduce it locally using the same versions as you but without success.

if I set my SONAR_HOST_URL environment variable to http://localhost:9000

❯ export SONAR_HOST_URL=http://localhost:9000
❯ echo $SONAR_HOST_URL
http://localhost:9000

And I ran my analysis invoking the sonar-scanner-cli like:

 ~/workspace/testProject
❯ sonar-scanner \
  -Dsonar.projectKey=EmptyProject3 \
  -Dsonar.sources=. \
  -Dsonar.login=sqa_c8f1bmytoken

I see in the scanner logs the url I saved in my sonar.core.serverBaseURL property:

...
...
INFO: Analysis report uploaded in 40ms
INFO: ANALYSIS SUCCESSFUL, you can find the results at: https://matteo.publicurl.io/dashboard?id=EmptyProject3
INFO: Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report
INFO: More about the report processing at https://matteo.publicurl.io/api/ce/task?id=AYf7cdB0xIZ12f
INFO: Analysis total time: 7.641 s
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
INFO: ------------------------------------------------------------------------

I will add now to my test setup also the integration with GitLab, in order to understand how things change.
At the moment I am facing the expected behaviour to see in the analysis logs too the expected URL and not what I defined in SONAR_HOST_URL.
I will update the post with my finding when using a setup closer to yours.

Thanks Matteo for looking into this! Since this may be useful, here is what we see in the logs after the scanner runs for an MR in Gitlab:

INFO: ------------- Check Quality Gate status
INFO: Waiting for the analysis report to be processed (max 300s)
INFO: QUALITY GATE STATUS: PASSED - View details on http://sonarqube.sonarqube.svc:9000/dashboard?id=balrog-74&pullRequest=1403
INFO: Analysis total time: 21.291 s
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
INFO: ------------------------------------------------------------------------
INFO: Total time: 22.794s
INFO: Final Memory: 60M/200M
INFO: ------------------------------------------------------------------------

99%+ of our analyses are run on MR’s in Gitlab (when the MR is opened or new commits are pushed and the target branch is main it triggers a gitlab pipeline that runs the scanner via docker).

I did a complete setup of a CI/CD pipeline in order to get closer to your setup but this is still not enough for reproducing the issue.

Everything points to my local SonarQube, so for that reason I used a public URL pointing to my machine in GitLab, while the sonar.core.serverBaseURL points to localhost:9000.

With this setup I can see in the logs of the GitLab job the expected URL with the value defined in sonar.core.serverBaseURL. I am using the latest (4.8.0) sonar-scanner-cli running on docker.

Running with gitlab-runner 15.9.0~beta.115.g598a7c91 (598a7c91)
  on blue-4.shared.runners-manager.gitlab.com/default J2nyww-s, system ID: s_5425356d8adf
  feature flags: FF_USE_IMPROVED_URL_MASKING:true

Preparing the "docker+machine" executor
Using Docker executor with image sonarsource/sonar-scanner-cli:latest ...
Pulling docker image sonarsource/sonar-scanner-cli:latest ...
Using docker image sha256:b54a89b534a10f0af24dab4d4b1e2d1d92ebfd15880b456e9039f05f92f70c87 for sonarsource/sonar-scanner-cli:latest with digest sonarsource/sonar-scanner-cli@sha256:589613d01625fb0a891eec712850f0ca0d0046e032e9ec8834885fc1c19f5736 ...
...
...
INFO: ------------- Check Quality Gate status
INFO: Waiting for the analysis report to be processed (max 300s)
INFO: QUALITY GATE STATUS: PASSED - View details on http://localhost:9000/dashboard?id=matteo.mara_testproject_AYf7h9jrxIZ12fPmiaON&pullRequest=1
INFO: Analysis total time: 34.695 s
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
INFO: ------------------------------------------------------------------------
INFO: Total time: 38.695s
INFO: Final Memory: 14M/56M
INFO: ------------------------------------------------------------------------

Saving cache for successful job
Creating cache sonarqube-check-non_protected...
.sonar/cache: found 22 matching artifact files and directories 
Uploading cache.zip to https://storage.googleapis.com/gitlab-com-runners-cache/project/45/sonarqube-check-non_protected 
Created cache

Cleaning up project directory and file based variables
00:01
Job succeeded

The same URLs can be seen in the MR decoration (that is the case also with your setup).

At this point I tried to redefine the sonar.core.serverBaseURL in a way that only the scanner could fetch, and I modified the .gitlab-ci.yml by adding the definition of the sonar.core.serverBaseURL there too (overriding the one defined in my instance)

  script:
    - sonar-scanner -Dsonar.qualitygate.wait=true -Dsonar.core.serverBaseURL=http://wrongUrl

Here the full file:

sonarqube-check:
  image:
    name: sonarsource/sonar-scanner-cli:latest
    entrypoint: [""]
  variables:
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"  # Defines the location of the analysis task cache
    GIT_DEPTH: "0"  # Tells git to fetch all the branches of the project, required by the analysis task
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .sonar/cache
  script:
    - sonar-scanner -Dsonar.qualitygate.wait=true -Dsonar.core.serverBaseURL=http://wrongUrl
  allow_failure: true
  rules:
    - if: $CI_COMMIT_REF_NAME == 'main' || $CI_PIPELINE_SOURCE == 'merge_request_event'

With this setup in place I managed to have two different values, in the scanner logs and in the decoration of the MR.

INFO: ------------- Check Quality Gate status
INFO: Waiting for the analysis report to be processed (max 300s)
INFO: QUALITY GATE STATUS: PASSED - View details on http://wrongurl/dashboard?id=matteo.mara_testproject_AYf7h9jrxIZ12fPmiaON&pullRequest=1
INFO: Analysis total time: 31.423 s
INFO: ------------------------------------------------------------------------

I would expect at this point that your gitlab-ci.yml file looks simlar to this and redefines the value of the serverBaseURL. Is there a reason why you are not relying on the value of the serverBaseURL returned by your SQ instance?

Our gitlab-ci.yml actually is this:

include:
  - local: sonarqube/base/shared.yml

sonarqube-check:
  image:
    name: "${SONAR_SCANNER_CONTAINER_VERSION}"
    entrypoint: [""]
  before_script: "${SONAR_BEFORE_SCRIPT}"
  script:
    # SonarQube does not automatically discover tests for languages that don't
    # do maven/gradle builds so we need to explicitly configure the directories
    # containing tests (hopefully these are all named with 'test' in there
    # somewhere...)
    - SONAR_TESTS="$(find . -type d -name '*test*' -prune -exec printf '%s/,' {} + | sed "s/\.\///g")"
    - SONAR_TESTS=${SONAR_TESTS:-,}
    - export SONAR_TESTS=${SONAR_TESTS::-1}
    - SONAR_EXCLUSIONS="$(find . -type d -name '*test*' -prune -exec printf '%s/*,' {} + | sed "s/\.\///g")"
    - SONAR_EXCLUSIONS="$(find . -type d -name '*test*' -prune -exec printf '%s/**/*,' {} + | sed "s/\.\///g")${SONAR_EXCLUSIONS}"
    - SONAR_EXCLUSIONS=${SONAR_EXCLUSIONS:-,}
    - export SONAR_EXCLUSIONS=${SONAR_EXCLUSIONS::-1}
    - sonar-scanner
      -Dsonar.qualitygate.wait=true
      -Dsonar.projectKey=${DANGER_SONAR_PROJECT_KEY}
      -Dsonar.python.version=3.10
      -Dsonar.sources=./
      -Dsonar.tests=${SONAR_TESTS}
      -Dsonar.exclusions=${SONAR_EXCLUSIONS}
      -Dsonar.coverage.exclusions=*,**/*
  rules:
    - !reference [.run_on_main_or_merge, rules]

variables:
  # Use CONTAINER_VERSION suffix so renovate will update this as a docker image
  SONAR_SCANNER_CONTAINER_VERSION: sonarsource/sonar-scanner-cli:4.8.0@sha256:0199cae1c763f27c783d45ad4819a33799250635cc3525615939439066b13283

and the sonarqube/base/shared.yml is this:

default:
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .sonar/cache

# Tells Sonar to only run on the main branch or on merge requests
#
# NOTE: The job-level configuration `allow_failure` does not support variables,
# so to make failure mode configurable this is the easiest workaround available
# https://gitlab.com/gitlab-org/gitlab-foss/-/issues/30232#note_672243242
.run_on_main_or_merge:
  rules:
    - if: "$DISABLE_SONAR_PIPELINE != 'true' && ($CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH || $CI_PIPELINE_SOURCE == 'merge_request_event') && $ALLOW_SONAR_FAILURE == 'true'"
      allow_failure: true
    - if: "$DISABLE_SONAR_PIPELINE != 'true' && ($CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH || $CI_PIPELINE_SOURCE == 'merge_request_event') && $ALLOW_SONAR_FAILURE != 'true'"
      allow_failure: false

variables:
  ALLOW_SONAR_FAILURE: "true"
  SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
  GIT_DEPTH: "0"
  SONAR_HOST_URL: "http://sonarqube.sonarqube.svc:9000"
  DANGER_SONAR_PROJECT_KEY: "${CI_PROJECT_NAME}-${CI_PROJECT_ID}"
  SONAR_BEFORE_SCRIPT: ""
  DISABLE_SONAR_PIPELINE: "false"

We’ve got it configured like this because most all of our projects are python-based and just want to auto-inherit a fully-working config. As you can see, we actually don’t overwrite the sonar.core.serverBaseURL in this anywhere. I did try this in one of our pipelines before creating this thread originally but it didn’t actually fix our issue so I removed it… :thinking:

Also, there is one difference in your setup vs mine. I configured the sonar.core.serverBaseURL in the values.yml when creating our helm deployment (via the sonarProperties key). When I log in to the admin UI, it actually does not display the configured value:

The same is true of other things that I configured during the helm deployment (e.g.: SAML authentication).

Thanks for these extra details @Jacob_Williams.

Now I manage to reproduce your behavior.
If I set the value of sonar.core.serverBaseURL in my sonar.properties file and not in the UI, I do see in the logs, the same value as SONAR_HOST_URL, while in the MR decoration I see the value of sonar.core.serverBaseURL being used for completing the rules.

I can say that for fixing your issue you can add in the Administration panel of your instance the same value as the one available in your values.yml file.
The one in the properties file will still be used by the MR decoration and will override the one in the UI, but by using the same value in both places you should see in the scanner logs the correct URL you are expecting.
Or if you don’t want to touch the UI, you can add to your script section the definition of the property, that value will be picked for the logs of the Job on GitLab.

I tested both behaviors and they produce the same result.

Via UI:

INFO: Analysis report uploaded in 420ms
INFO: ------------- Check Quality Gate status
INFO: Waiting for the analysis report to be processed (max 300s)
INFO: QUALITY GATE STATUS: PASSED - View details on http://localhost3:9000/dashboard?id=matteo.mara_testproject_AYf7h9jrxIZ12fPmiaON&pullRequest=1
INFO: Time spent writing ucfgs 8ms
INFO: Analysis total time: 45.340 s
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
INFO: ------------------------------------------------------------------------
INFO: Total time: 50.274s
INFO: Final Memory: 78M/196M
INFO: ------------------------------------------------------------------------

via the gitlab-ci.yml file

  script:
    - sonar-scanner -Dsonar.qualitygate.wait=true -Dsonar.core.serverBaseURL=http://localhost2:9000
INFO: ------------- Check Quality Gate status
INFO: Waiting for the analysis report to be processed (max 300s)
INFO: QUALITY GATE STATUS: PASSED - View details on http://localhost2:9000/dashboard?id=matteo.mara_testproject_AYf7h9jrxIZ12fPmiaON&pullRequest=1
INFO: Time spent writing ucfgs 8ms
INFO: Analysis total time: 44.830 s
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
INFO: ------------------------------------------------------------------------
INFO: Total time: 49.578s
INFO: Final Memory: 78M/198M
INFO: ------------------------------------------------------------------------

I will follow up on this on our side because there is room for improvement in terms of consistency.
I created a Jira for it: SONAR-19280

1 Like

@Matteo_Mara thank you for following up, reproducing the issue, suggesting a workaround, and filing that ticket! I’ve set the value in the UI for now and will keep an eye on that Jira.

1 Like

I modified my previous answer in order to provide a programmatic workaround too in case dealing with the UI adds more friction.
I confirm that passing the value also with -D when running sonar-scanner, will result in the correct output in the logs of the GitLab job.