diff --git a/.gitea/workflows/sonar-scan.yaml b/.gitea/workflows/sonar-scan.yaml index 61e0c33..57d5a08 100644 --- a/.gitea/workflows/sonar-scan.yaml +++ b/.gitea/workflows/sonar-scan.yaml @@ -2,40 +2,34 @@ name: SonarQube Analysis on: push: - branches: - - main pull_request: types: [opened, synchronize, reopened] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: sonarqube: name: Build, Test & Analyse runs-on: ubuntu-latest + timeout-minutes: 15 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' - + - name: Make Maven wrapper executable run: chmod +x mvnw - # ------------------------------------------------------------------ # - # 3. Cache Maven local repository and the SonarQube analysis cache # - # ------------------------------------------------------------------ # - name: Cache Maven packages uses: actions/cache@v4 with: @@ -50,73 +44,48 @@ jobs: 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: Validate required secrets + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + SONAR_PROJECT_KEY: ${{ secrets.SONAR_PROJECT_KEY }} + run: | + [[ -n "$SONAR_TOKEN" ]] || { echo "::error::SONAR_TOKEN is not set"; exit 1; } + [[ -n "$SONAR_HOST_URL" ]] || { echo "::error::SONAR_HOST_URL is not set"; exit 1; } + [[ -n "$SONAR_PROJECT_KEY" ]] || { echo "::error::SONAR_PROJECT_KEY is not set"; exit 1; } + - name: Build and test run: | ./mvnw -B verify \ - -Dtest='!PostgresIntegrationTests' + -Dtest='!PostgresIntegrationTests,!MySqlIntegrationTests' - name: SonarQube analysis env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + SONAR_PROJECT_KEY: ${{ secrets.SONAR_PROJECT_KEY }} run: | - ./mvnw -B sonar:sonar \ - -Dsonar.projectKey=${{ secrets.SONAR_PROJECT_KEY }} \ - -Dsonar.projectName="Spring PetClinic" \ + ./mvnw -B org.sonarsource.scanner.maven:sonar-maven-plugin:4.0.0.4121:sonar \ + -Dsonar.projectKey="${SONAR_PROJECT_KEY}" \ -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: ${{ secrets.SONAR_HOST_URL }} + SONAR_PROJECT_KEY: ${{ secrets.SONAR_PROJECT_KEY }} 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=${{ secrets.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..." + for i in $(seq 1 24); do + RESPONSE=$(curl -sf -u "${SONAR_TOKEN}:" \ + "${SONAR_HOST_URL}/api/qualitygates/project_status?projectKey=${SONAR_PROJECT_KEY}" || true) + STATUS=$(echo "$RESPONSE" | jq -r '.projectStatus.status' 2>/dev/null || echo "NONE") + if [[ "$STATUS" =~ ^(OK|ERROR|WARN)$ ]]; then break; fi + echo " Status: ${STATUS:-pending} — retrying in 5s..." sleep 5 done - echo "Quality Gate status: $STATUS" - if [ "$STATUS" = "ERROR" ]; then - echo "❌ Quality Gate FAILED — check ${{ secrets.SONAR_HOST_URL }}/dashboard?id=${{ secrets.SONAR_PROJECT_KEY }}" - exit 1 - elif [ "$STATUS" = "OK" ]; then - echo "✅ Quality Gate PASSED" - else - echo "⚠️ Quality Gate status: $STATUS" - fi \ No newline at end of file + [[ "$STATUS" != "ERROR" ]] || { echo "::error::Quality Gate FAILED"; exit 1; } \ No newline at end of file