name: SonarQube Analysis on: push: branches: - main pull_request: types: [opened, synchronize, reopened] jobs: sonarqube: name: Build, Test & Analyse runs-on: ubuntu-latest steps: # ------------------------------------------------------------------ # # 1. Full checkout — shallow clones degrade SonarQube blame/new-code # # ------------------------------------------------------------------ # - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 # ------------------------------------------------------------------ # # 2. Java 17 — matches spring-petclinic's java.version property # # ------------------------------------------------------------------ # - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' # ------------------------------------------------------------------ # # 3. Cache Maven local repository and the SonarQube analysis cache # # ------------------------------------------------------------------ # - name: Cache Maven packages uses: actions/cache@v4 with: path: ~/.m2/repository key: maven-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: maven-${{ runner.os }}- - name: Cache SonarQube analysis data uses: actions/cache@v4 with: path: ~/.sonar/cache key: sonar-${{ runner.os }} restore-keys: sonar-${{ runner.os }} # ------------------------------------------------------------------ # # 4. Build, run tests, collect JaCoCo coverage, then push to Sonar. # # # # Why one command? # # - `verify` compiles, runs unit + integration tests, and lets # # JaCoCo write target/site/jacoco/jacoco.xml # # - `sonar:sonar` reads the compiled bytecode, test results, and # # coverage report that `verify` just produced # # # # The H2 in-memory database is active by default so no external DB # # service is needed for the standard test suite. # # # # Required secrets (Settings → Secrets): # # SONAR_TOKEN – SonarQube user / project token # # # # Required variables (Settings → Variables): # # SONAR_HOST_URL – e.g. http://sonarqube.example.com:9000 # # SONAR_PROJECT_KEY – e.g. spring-petclinic # # ------------------------------------------------------------------ # - name: Build, test and analyse with SonarQube env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} run: | ./mvnw -B verify sonar:sonar \ -Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }} \ -Dsonar.projectName="Spring PetClinic" \ -Dsonar.host.url="${SONAR_HOST_URL}" \ -Dsonar.token="${SONAR_TOKEN}" \ -Dsonar.java.source=17 \ -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml # ------------------------------------------------------------------ # # 5. Quality Gate — poll until the result is ready, fail if red # # ------------------------------------------------------------------ # - name: Quality Gate check env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} run: | echo "Waiting for SonarQube to process the analysis..." STATUS="" for i in $(seq 1 24); do # up to ~2 minutes RESPONSE=$(curl -sf \ -u "${SONAR_TOKEN}:" \ "${SONAR_HOST_URL}/api/qualitygates/project_status?projectKey=${{ vars.SONAR_PROJECT_KEY }}" \ || true) STATUS=$(echo "$RESPONSE" | python3 -c \ "import sys,json; print(json.load(sys.stdin)['projectStatus']['status'])" \ 2>/dev/null || echo "NONE") if [ "$STATUS" = "OK" ] || [ "$STATUS" = "ERROR" ] || [ "$STATUS" = "WARN" ]; then break fi echo " Status: ${STATUS:-pending} — retrying in 5 s..." sleep 5 done echo "Quality Gate status: $STATUS" if [ "$STATUS" = "ERROR" ]; then echo "❌ Quality Gate FAILED — check ${{ vars.SONAR_HOST_URL }}/dashboard?id=${{ vars.SONAR_PROJECT_KEY }}" exit 1 elif [ "$STATUS" = "OK" ]; then echo "✅ Quality Gate PASSED" else echo "⚠️ Quality Gate status: $STATUS" fi