refactor!: repackage dev.sonarcli -> io.github.randomcodespace.sonarpredict #17
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Scan parity (self-scan ↔ SonarQube Cloud) | |
| # Runs BOTH scans on the same commit and diffs their issue lists. Every PR | |
| # answers: "does our daemon find what SonarSource's own pipeline finds?". | |
| # | |
| # The standalone self-scan (sonar.yml) is kept because it works on fork PRs | |
| # (no SONAR_TOKEN needed), whereas this one requires the token and so skips | |
| # for forks. | |
| # | |
| # Setup required (one-time, by the repo admin): | |
| # 1. Sign in to https://sonarcloud.io with the repo's GitHub org. | |
| # 2. Import this repo as a SonarQube Cloud project. | |
| # Project key: RandomCodeSpace_sonar-predict, organisation: randomcodespace | |
| # (adjust the env values below if SonarCloud assigns different ones). | |
| # 3. Configure "Analysis Method" → "With GitHub Actions"; copy SONAR_TOKEN. | |
| # 4. Add SONAR_TOKEN as a repo secret. | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| env: | |
| SONAR_PROJECT_KEY: RandomCodeSpace_sonar-predict | |
| SONAR_ORGANIZATION: randomcodespace | |
| SONAR_HOST_URL: https://sonarcloud.io | |
| jobs: | |
| parity: | |
| name: Scan parity | |
| runs-on: ubuntu-latest | |
| # Skip when the token isn't reachable (fork PRs) — the standalone | |
| # self-scan workflow still gates fork PRs on our daemon's findings. | |
| if: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }} | |
| env: | |
| # The assembly step packages the ~150 MB distribution bundle (CLI + | |
| # daemon fat jars + 10 analyzer plugins). 2 GB heap keeps maven-assembly-plugin | |
| # from OOMing while it zips the bundle. | |
| MAVEN_OPTS: -Xmx2g | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: '17' | |
| - name: Set up Node.js 20 | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Cache Maven repository | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.m2/repository | |
| key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-maven- | |
| - name: Cache SonarQube Cloud packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.sonar/cache | |
| key: ${{ runner.os }}-sonar | |
| restore-keys: | | |
| ${{ runner.os }}-sonar | |
| # Fail fast if the SONAR_TOKEN secret isn't configured. The Cloud step | |
| # below would just error obscurely; this is a clearer signal. | |
| - name: Verify SONAR_TOKEN | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| run: | | |
| if [ -z "${SONAR_TOKEN:-}" ]; then | |
| echo "::error::SONAR_TOKEN not set — parity workflow needs SonarQube Cloud access. See the header of this workflow for setup." | |
| exit 1 | |
| fi | |
| echo "SONAR_TOKEN configured." | |
| # Single build that both scans then consume. `verify` produces the | |
| # JaCoCo XML that both scans use as coverage evidence, plus the | |
| # distribution bundle the self-scan invokes via its bin/sonar launcher. | |
| - name: Build and test (generates JaCoCo XML + distribution bundle) | |
| run: mvn -B -ntp clean verify -Dsurefire.failIfNoSpecifiedTests=false | |
| # Derive the project version from pom.xml so SONAR_PREDICTOR_HOME below | |
| # tracks pom bumps without anyone touching CI. | |
| - name: Derive project version | |
| id: version | |
| run: | | |
| set -euo pipefail | |
| VERSION=$(mvn -B -ntp -q -DforceStdout help:evaluate -Dexpression=project.version) | |
| if [ -z "${VERSION}" ]; then | |
| echo "::error::could not derive project.version from pom.xml" | |
| exit 1 | |
| fi | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "Project version: ${VERSION}" | |
| # --- (A) Self-scan ------------------------------------------------------ | |
| - name: Run self-scan | |
| run: | | |
| set +e | |
| export SONAR_PREDICTOR_HOME="$(pwd)/target/sonar-predictor-dist-${{ steps.version.outputs.version }}/sonar-predictor" | |
| mkdir -p .sonar-predictor | |
| "$SONAR_PREDICTOR_HOME/bin/sonar" \ | |
| --format json --save .sonar-predictor/scan.json \ | |
| analyze . --coverage target/site/jacoco/jacoco.xml | |
| rc=$? | |
| set -e | |
| echo "Self-scan exit code: $rc (0=clean, 1=issues found, 2+=tool error)" | |
| if [ "$rc" -ge 2 ]; then | |
| echo "::error::Self-scan tool error (exit $rc)" | |
| exit "$rc" | |
| fi | |
| # --- (B) SonarQube Cloud scan ------------------------------------------ | |
| - name: Run SonarQube Cloud scan | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| mvn -B -ntp \ | |
| org.sonarsource.scanner.maven:sonar-maven-plugin:sonar \ | |
| -Dsonar.projectKey="${SONAR_PROJECT_KEY}" \ | |
| -Dsonar.organization="${SONAR_ORGANIZATION}" \ | |
| -Dsonar.host.url="${SONAR_HOST_URL}" \ | |
| -Dsonar.qualitygate.wait=false \ | |
| -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml | |
| # SonarQube Cloud processes the scan asynchronously after upload. Poll | |
| # the last completed analysis until the timestamp moves past the start | |
| # of this run, or give up after ~3 minutes (most analyses complete in | |
| # 30-60s; longer polls aren't worth holding the runner for). | |
| - name: Wait for SonarQube Cloud processing | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| run: | | |
| START_TS=$(date +%s) | |
| for attempt in $(seq 1 18); do | |
| # Constrain both the initial request (--proto =https) AND any redirect | |
| # (--proto-redir =https) to HTTPS. Without --proto, githubactions:S6506 | |
| # still fires even when SONAR_HOST_URL is already https://; the rule | |
| # wants both belts explicit at the curl-call site. | |
| RESP=$(curl -fsSL --proto =https --proto-redir =https -u "$SONAR_TOKEN:" \ | |
| "${SONAR_HOST_URL}/api/project_analyses/search?project=${SONAR_PROJECT_KEY}&ps=1" || echo '{}') | |
| ANALYSIS_TS=$(echo "$RESP" | jq -r '.analyses[0].date // empty' || true) | |
| if [ -n "$ANALYSIS_TS" ]; then | |
| ANALYSIS_EPOCH=$(date -d "$ANALYSIS_TS" +%s 2>/dev/null || echo 0) | |
| if [ "$ANALYSIS_EPOCH" -ge "$START_TS" ]; then | |
| echo "Latest analysis ($ANALYSIS_TS) is from this run." | |
| exit 0 | |
| fi | |
| echo "Attempt $attempt: latest analysis is $ANALYSIS_TS (before run start), waiting…" | |
| else | |
| echo "Attempt $attempt: no analyses yet on SonarQube Cloud, waiting…" | |
| fi | |
| sleep 10 | |
| done | |
| echo "::warning::Timed out waiting for SonarQube Cloud to publish this run's analysis. Parity diff will use the most-recent published state." | |
| # --- (C) Parity diff ---------------------------------------------------- | |
| - name: Diff self-scan vs SonarQube Cloud | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| run: | | |
| PR_ARG="" | |
| BRANCH_ARG="" | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| PR_ARG="--pull-request ${{ github.event.pull_request.number }}" | |
| else | |
| BRANCH_ARG="--branch ${{ github.ref_name }}" | |
| fi | |
| python3 scripts/scan_parity.py \ | |
| --self-scan .sonar-predictor/scan.json \ | |
| --project-key "${SONAR_PROJECT_KEY}" \ | |
| --organization "${SONAR_ORGANIZATION}" \ | |
| --host "${SONAR_HOST_URL}" \ | |
| $PR_ARG $BRANCH_ARG \ | |
| --out .sonar-predictor/parity.json | |
| - name: Upload scan + parity artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: scan-parity-${{ github.run_id }} | |
| path: | | |
| .sonar-predictor/scan.json | |
| .sonar-predictor/parity.json | |
| retention-days: 14 | |
| - name: Link to SonarQube Cloud dashboard | |
| if: always() | |
| run: | | |
| { | |
| echo "" | |
| echo "---" | |
| echo "" | |
| echo "**SonarQube Cloud dashboard:** ${SONAR_HOST_URL}/project/overview?id=${SONAR_PROJECT_KEY}" | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| echo "" | |
| echo "**PR-scoped view:** ${SONAR_HOST_URL}/project/issues?id=${SONAR_PROJECT_KEY}&pullRequest=${{ github.event.pull_request.number }}" | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" |