Featured image of post Integrating SonarQube into Golang Projects for Code Quality Analysis

Integrating SonarQube into Golang Projects for Code Quality Analysis

Test Coverage and Code Quality Analysis

Installation and Configuration

Installation

Configure Gitlab Login

  • Documentation: https://docs.sonarsource.com/sonarqube/10.1/instance-administration/authentication/gitlab/

    • Create application at https://<Your Gitlab URL>/-/profile/applications (read_user permission)
    • Callback URL must be <Your SonarQube URL>/oauth2/callback/gitlab
  • Gitlab login button should appear after configuration

    • Server base URL must be configured for successful redirection
    • Configure Gitlab project import
    • Create Token at https://<Your Gitlab URL>/-/profile/personal_access_tokens
    • Gitlab API endpoint: https://<Your Gitlab URL>/api/v4
    • Gitlab option will appear in project creation after successful configuration
  • Multi-branch configuration (default single branch only)

Project Configuration

  • Prepare three configurations in SonarQube (custom variable names allowed):
    • SONAR_HOST_URL= (SonarQube URL)
    • SONAR_PROJECT_KEY= (Project identifier)
    • SONAR_TOKEN= (Project token created in final setup step)
    • Add these variables to Gitlab CI/CD

.gitlab-ci.yml

sonarqube-check:
  stage: check
  image:
    name: sonarsource/sonar-scanner-cli:latest
    entrypoint: [""]
  variables:
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
    GIT_DEPTH: "0"
    SONAR_HOST_URL: "${SONAR_HOST_URL}"
    SONAR_TOKEN: "${SONAR_TOKEN}"
    SONAR_PROJECT_KEY: "${SONAR_PROJECT_KEY}"
  allow_failure: false
  only:
    - main
    - dev
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .sonar/cache
  coverage: '/total:\s+\(statements\)\s+\d+.\d+%/'
  script:
    - bash ./deployment/sonarqube-check.sh
    - sonar-scanner -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.token=$SONAR_TOKEN -Dsonar.projectKey=$SONAR_PROJECT_KEY -Dsonar.branch.name=$CI_COMMIT_REF_NAME

.golangci.yml

run:
  concurrency: 4
  timeout: 5m
  issues-exit-code: 1
  tests: true

output:
  print-issued-lines: true
  print-linter-name: true
  uniq-by-line: true
  sort-results: false

linters:
  disable-all: true
  enable:
    - cyclop
    - gocognit
    - gocyclo
    - maintidx
    - gocritic
    - ineffassign
    - durationcheck
    - errcheck
    - errorlint
    - exhaustive
    - exportloopref
    - funlen
    - goconst
    - lll
    - nilnil
    - prealloc
    - promlinter
    - grouper
    - varnamelen
    - importas
    - forcetypeassert
    - decorder
    - errname
    - err113
    - gofmt
    - goimports
    - gomodguard
    - goprintffuncname
    - govet
    - misspell
    - nakedret
    - nestif
    - predeclared
    - unconvert
    - thelper

linters-settings:
  cyclop:
    max-complexity: 30
    skip-tests: false
  gocognit:
    min-complexity: 30
  gocyclo:
    min-complexity: 30
  dogsled:
    max-blank-identifiers: 2
  errcheck:
    check-type-assertions: true
    check-blank: true
    exclude-functions:
      - io.Copy(*bytes.Buffer)
      - io.Copy(os.Stdout)
  errorlint:
    errorf: true
    asserts: true
    comparison: true

  exhaustive:
    default-signifies-exhaustive: false

  funlen:
    lines: 100
    statements: 40

  goconst:
    min-len: 3
    min-occurrences: 3
    ignore-tests: false
    match-constant: true
    numbers: true
    min: 3
    max: 99999
    ignore-calls: false

  gofmt:
    simplify: true

  lll:
    line-length: 120
    tab-width: 1

  makezero:
    always: false
  nestif:
    min-complexity: 4

  nilnil:
    checked-types:
      - ptr
      - func
      - iface
      - map
      - chan

  nlreturn:
    block-size: 1
  varnamelen:
    max-distance: 9
    min-name-length: 1

  predeclared:
    ignore: ""
    q: false

  staticcheck:
    checks: [ "all" ]

  stylecheck:
    checks: [ "all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022" ]
    dot-import-whitelist:
      - fmt
    initialisms: [ "ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS" ]
    http-status-code-whitelist: [ "200", "400", "404", "500" ]

  unparam:
    check-exported: false

  unused:
    exported-is-used: false
    exported-fields-are-used: false

./deployment/sonarqube-check.sh

#!/bin/bash

go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct

go mod tidy

## Code quality check
golangci-lint run ./... --out-format checkstyle > report.xml
ret01=$?
if [ $ret01 -ne 0 ]; then
    echo -e "\033[31m  >>>> Code quality check failed, exiting <<<<  \033[0m"
    exit 1
fi

## Test coverage
go test ./... -coverprofile="coverage.cov" -covermode count
ret02=$?
if [ $ret02 -ne 0 ]; then
    echo -e "\033[31m  >>>> Test coverage check failed, exiting <<<<  \033[0m"
    exit 2
fi

## Output coverage for Gitlab parsing
go tool cover -func="coverage.cov"

## Unit tests
go test -json ./... > report.json
ret03=$?
if [ $ret03 -ne 0 ]; then
    echo -e "\033[31m  >>>> Unit tests failed, exiting <<<<  \033[0m"
    exit 3
fi

exit 0

Final Results

  • After configuration, code quality metrics will be visible in Gitlab