Skip to main content

Check out Port for yourselfΒ 

Auto-label your GitHub PRs with Sonar Scans

This guide demonstrates how to set up an automation in Port that applies color-coded labels to your GitHub pull requests based on SonarCloud scan reports. These labels help you classify vulnerabilities, code smells, security hotspots, and bugs right from the pull request view.

Common use cases​

  • Enforce code quality standards: Highlight PRs with poor test coverage, high duplication, or critical issues.
  • Encourage developer accountability: Make quality regressions visible and traceable at the PR level.

Prerequisites​

This guide assumes the following:

Set up data model​

To connect scan data with the correct pull requests, you'll need to link the Pull Request blueprint with the SonarQube Analysis blueprint.

Follow the Connect GitHub PR to SonarQube analysis guide to set this up.

Set up automation​

Once the SonarQube scan entities are linked to their corresponding pull requests, you can configure an automation in Port that triggers a GitHub workflow on PR updates and applies Sonar-based labels directly to the pull request.

This setup involves two parts:

  1. Adding a GitHub PAT as a Port secret.

  2. Defining the automation in Port.

Add Port secrets​

To add a secret to your portal:

  1. Click on the ... button in the top right corner of your Port application.

  2. Click on Credentials.

  3. Click on the Secrets tab.

  4. Click on + Secret and add the following secrets:

Define automation backend​

  1. Go to the Automations page in your portal.

  2. Click on the + Automation button.

  3. Copy and paste the following JSON configuration into the editor:

    Apply SonarCloud label automation (Click to expand)
    Replace placeholders

    Make sure to replace <YOUR_GITHUB_ORG> and <YOUR_GITHUB_REPO> in the url field below with the actual organization and repository where your apply-sonar-scan-on-pr.yaml workflow resides.

    {
    "identifier": "addLabelOnGithubPR",
    "title": "Add Sonar Scan Label On PR Updated",
    "description": "Automation to add Sonar scan label to the GitHub PR upon update",
    "trigger": {
    "type": "automation",
    "event": {
    "type": "ENTITY_UPDATED",
    "blueprintIdentifier": "githubPullRequest"
    },
    "condition": {
    "type": "JQ",
    "expressions": [
    ".diff.after.relations.sonarAnalysis != null"
    ],
    "combinator": "and"
    }
    },
    "invocationMethod": {
    "type": "WEBHOOK",
    "url": "https://api.github.com/repos/<YOUR_GITHUB_ORG>/<YOUR_GITHUB_REPO>/actions/workflows/apply-sonar-scan-on-pr.yaml/dispatches",
    "method": "POST",
    "headers": {
    "Accept": "application/vnd.github+json",
    "Authorization": "Bearer {{ .secrets.GITHUB_TOKEN }}",
    "Content-Type": "application/json"
    },
    "body": {
    "ref": "main",
    "inputs": {
    "prNumber": "{{ .event.diff.after.properties.prNumber | tostring }}",
    "repository": "{{ .event.diff.after.relations.repository }}",
    "sonarEntity": "{{ .event.diff.after.relations.sonarAnalysis }}"
    }
    }
    },
    "publish": true
    }
  4. Click Save.

Create the GitHub workflow​

Now let us define the GitHub Actions workflow that receives the input and applies labels to the pull request.

Dedicated Workflows Repository

We recommend creating a dedicated repository for the workflows that are used by Port actions.

In your dedicated workflow repository, ensure you have a .github/workflows directory.

  1. Create a new file named apply-sonar-scan-on-pr.yaml

  2. Copy and paste the following workflow configuration:

    Apply SonarCloud labels workflow (Click to expand)

    name: Apply Sonar Scan on PR

    on:
    workflow_dispatch:
    inputs:
    prNumber:
    required: true
    type: string
    repository:
    required: true
    type: string
    sonarEntity:
    required: true
    type: string

    jobs:
    analyze_sonar:
    runs-on: ubuntu-latest
    env:
    PORT_CLIENT_ID: ${{ secrets.PORT_CLIENT_ID }}
    PORT_CLIENT_SECRET: ${{ secrets.PORT_CLIENT_SECRET }}
    GH_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }}
    steps:
    - name: Checkout
    uses: actions/checkout@v4

    - name: Fetch Port Access Token
    id: fetch_port_token
    run: |
    PORT_ACCESS_TOKEN=$(curl -s -L 'https://api.getport.io/v1/auth/access_token' \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json' \
    -d '{
    "clientId": "${{ secrets.PORT_CLIENT_ID }}",
    "clientSecret": "${{ secrets.PORT_CLIENT_SECRET }}"
    }' | jq -r '.accessToken')
    echo "PORT_ACCESS_TOKEN=$PORT_ACCESS_TOKEN" >> "$GITHUB_ENV"

    - name: Get Sonar Entity from Port
    id: get_sonar
    run: |
    sonar_entity_id="${{ github.event.inputs.sonarEntity }}"
    echo "πŸ” Fetching Sonar entity $sonar_entity_id"

    sonar_response=$(curl -s -X GET "https://api.port.io/v1/blueprints/sonarQubeAnalysis/entities/$sonar_entity_id" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ${{ env.PORT_ACCESS_TOKEN }}")

    echo "$sonar_response"

    FIXED_ISSUES=$(echo "$sonar_response" | jq '.entity.properties.fixedIssues // 0')
    NEW_ISSUES=$(echo "$sonar_response" | jq '.entity.properties.newIssues // 0')
    COVERAGE=$(echo "$sonar_response" | jq '.entity.properties.coverage // 0')
    DUPLICATIONS=$(echo "$sonar_response" | jq '.entity.properties.duplications // 0')

    echo "FIXED_ISSUES=$FIXED_ISSUES" >> "$GITHUB_ENV"
    echo "NEW_ISSUES=$NEW_ISSUES" >> "$GITHUB_ENV"
    echo "COVERAGE=$COVERAGE" >> "$GITHUB_ENV"
    echo "DUPLICATIONS=$DUPLICATIONS" >> "$GITHUB_ENV"

    - name: Classify and Apply Sonar Labels
    run: |
    set -e

    repo="${{ github.event.inputs.repository }}"
    owner="${{ github.repository_owner }}"
    pr_number=$(echo "${{ github.event.inputs.prNumber }}" | grep -o '[0-9]\+$')

    # Classify coverage
    if (( $(echo "$COVERAGE < 25" | bc -l) )); then
    coverage_label="Sonar: Coverage - 0-25%"
    elif (( $(echo "$COVERAGE < 50" | bc -l) )); then
    coverage_label="Sonar: Coverage - 25-50%"
    elif (( $(echo "$COVERAGE < 75" | bc -l) )); then
    coverage_label="Sonar: Coverage - 50-75%"
    else
    coverage_label="Sonar: Coverage - 75-100%"
    fi

    # Classify new issues
    if (( NEW_ISSUES == 0 )); then
    new_issues_label="Sonar: Issues - A"
    elif (( NEW_ISSUES <= 5 )); then
    new_issues_label="Sonar: Issues - B"
    elif (( NEW_ISSUES <= 10 )); then
    new_issues_label="Sonar: Issues - C"
    elif (( NEW_ISSUES <= 20 )); then
    new_issues_label="Sonar: Issues - D"
    else
    new_issues_label="Sonar: Issues - E"
    fi

    # Classify fixed issues
    if (( FIXED_ISSUES == 0 )); then
    fixed_issues_label="Sonar: Fixed - A"
    elif (( FIXED_ISSUES <= 5 )); then
    fixed_issues_label="Sonar: Fixed - B"
    elif (( FIXED_ISSUES <= 10 )); then
    fixed_issues_label="Sonar: Fixed - C"
    elif (( FIXED_ISSUES <= 20 )); then
    fixed_issues_label="Sonar: Fixed - D"
    else
    fixed_issues_label="Sonar: Fixed - E"
    fi

    # Classify duplications
    if (( $(echo "$DUPLICATIONS < 5" | bc -l) )); then
    dup_label="Sonar: Duplication - A"
    elif (( $(echo "$DUPLICATIONS < 10" | bc -l) )); then
    dup_label="Sonar: Duplication - B"
    elif (( $(echo "$DUPLICATIONS < 20" | bc -l) )); then
    dup_label="Sonar: Duplication - C"
    elif (( $(echo "$DUPLICATIONS < 30" | bc -l) )); then
    dup_label="Sonar: Duplication - D"
    else
    dup_label="Sonar: Duplication - E"
    fi

    labels_to_apply=("$coverage_label" "$new_issues_label" "$fixed_issues_label" "$dup_label")

    echo "🏷️ Will apply labels: ${labels_to_apply[*]}"

    # Define a function to assign colors based on grade
    get_label_color() {
    label="$1"
    if [[ "$label" == *" - A" || "$label" == *"75-100%" ]]; then
    echo "2cbe4e" # Green
    elif [[ "$label" == *" - B" || "$label" == *"50-75%" ]]; then
    echo "a2eeef" # Light blue
    elif [[ "$label" == *" - C" || "$label" == *"25-50%" ]]; then
    echo "fbca04" # Yellow
    elif [[ "$label" == *" - D" || "$label" == *"0-25%" ]]; then
    echo "f66a0a" # Orange
    else
    echo "d73a4a" # Red for E or anything else
    fi
    }

    # Create labels if they don’t exist, using dynamic colors
    for label in "${labels_to_apply[@]}"; do
    color=$(get_label_color "$label")
    echo "πŸ› οΈ Ensuring label exists: $label with color #$color"
    curl -s -o /dev/null -w "%{http_code}" -X POST "https://api.github.com/repos/$owner/$repo/labels" \
    -H "Authorization: Bearer $GH_TOKEN" \
    -H "Accept: application/vnd.github+json" \
    -d "{\"name\": \"$label\", \"color\": \"$color\"}" | grep -qE "201|422"
    done

    # Apply to PR
    echo "🏷️ Applying labels to PR #$pr_number..."
    curl -s -X POST "https://api.github.com/repos/$owner/$repo/issues/$pr_number/labels" \
    -H "Authorization: Bearer $GH_TOKEN" \
    -H "Accept: application/vnd.github+json" \
    -d "{\"labels\": [\"${labels_to_apply[0]}\", \"${labels_to_apply[1]}\", \"${labels_to_apply[2]}\", \"${labels_to_apply[3]}\"]}"
    Required GitHub Secrets

    For this workflow to function properly, you need to add the following secrets to your GitHub repository:

    • PORT_CLIENT_ID: The client ID of your Port account.
    • PORT_CLIENT_SECRET: The client ID of your Port account.
    • MY_GITHUB_TOKEN: The fine grained GitHub personal access token with Read and Write access to issues, pull requests across all repositories in your organization.
  3. Commit and push the changes to your repository.

Once a pull request associated with a SonarCloud analysis is updated, the automation will be triggered automatically. It will evaluate the latest scan results and apply color-coded labels to the PR, reflecting the quality status of the code.