Build: Add SonarCloud CI workflow (#22960)
* Add SonarCloud CI workflow Adds a manual-dispatch GitHub Actions workflow for SonarQube Cloud analysis (build, unit test coverage, scan). Moves file_header_template and SA1636/SA1633 suppression from .editorconfig comments and .globalconfig into the active .editorconfig .NET language conventions section, removing the duplicated suppression from .globalconfig. * Remove branch filter from pull_request trigger in SonarCloud workflow Runs analysis on all PRs regardless of target branch. * Adjust sonarcloud gh action based on feedback * Add .sonarqube to .gitignore * Attempt to split build and analysis in order to be able to run in PRs from forks * Adjust SonarCloud workflows * Rename SonarCloud workflows to reflect their actual purpose * Remove sonar.coverage.exclusions * Include .github in sonar analysis * Include build directory in sonar analysis * Apply sonarcloud workflow fixes from test branch * Remove setup-dotnet step from upload workflow * Use default branch from context instead of hardcoded main in analysis workflow * Update checkout action to v6 in upload workflow * Add actions: read permission to upload workflow * Enable SCM integration in upload workflow
This commit is contained in:
+4
-12
@@ -70,18 +70,6 @@ trim_trailing_whitespace = true
|
||||
[*.less]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
##########################################
|
||||
# File Header (Uncomment to support file headers)
|
||||
# https://docs.microsoft.com/visualstudio/ide/reference/add-file-header
|
||||
##########################################
|
||||
|
||||
# [*.{cs,csx,cake,vb,vbx}]
|
||||
file_header_template = Copyright (c) Umbraco.\nSee LICENSE for more details.
|
||||
|
||||
# SA1636: File header copyright text should match
|
||||
# Justification: .editorconfig supports file headers. If this is changed to a value other than "none", a stylecop.json file will need to added to the project.
|
||||
# dotnet_diagnostic.SA1636.severity = none
|
||||
|
||||
##########################################
|
||||
# .NET Language Conventions
|
||||
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions
|
||||
@@ -136,6 +124,10 @@ dotnet_code_quality_unused_parameters = all:warning
|
||||
dotnet_style_operator_placement_when_wrapping = end_of_line
|
||||
# https://github.com/dotnet/roslyn/pull/40070
|
||||
dotnet_style_prefer_simplified_interpolation = true:warning
|
||||
# File header preferences
|
||||
file_header_template = Copyright (c) Umbraco.\nSee LICENSE for more details.
|
||||
dotnet_diagnostic.SA1633.severity = none # Suppressed until we decide to enforce it
|
||||
dotnet_diagnostic.SA1636.severity = none # Suppressed since we are using StyleCop
|
||||
|
||||
# C# Code Style Settings
|
||||
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-code-style-settings
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
name: "SonarQube Cloud - Analysis"
|
||||
|
||||
# This workflow runs without secrets so it is safe to trigger on fork PRs.
|
||||
# It compiles the solution, runs unit tests, and uploads the Roslyn analysis
|
||||
# output as an artifact. The upload workflow then picks it up and uploads
|
||||
# results to SonarCloud using the SONAR_TOKEN secret.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- "v*/dev"
|
||||
- "v*/main"
|
||||
- "release/*"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
SONAR_PROJECT_KEY: umbraco_Umbraco-CMS
|
||||
SONAR_ORGANIZATION: umbraco
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and collect analysis data
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET from global.json
|
||||
uses: actions/setup-dotnet@v5
|
||||
|
||||
- name: Cache SonarQube packages
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
restore-keys: ${{ runner.os }}-sonar
|
||||
|
||||
- name: Install tools
|
||||
run: |
|
||||
dotnet tool install --global dotnet-sonarscanner
|
||||
dotnet tool install --global dotnet-coverage
|
||||
|
||||
- name: Load sonar params
|
||||
env:
|
||||
IS_FORK: ${{ github.event.pull_request.head.repo.fork }}
|
||||
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
||||
run: |
|
||||
if [ "$IS_FORK" = "true" ]; then
|
||||
# For fork PRs, read sonar params from the default branch.
|
||||
echo "SONARQUBE_SCANNER_PARAMS=$(git show origin/$DEFAULT_BRANCH:.github/workflows/sonarcloud/sonar-params.json | jq -c .)" >> $GITHUB_ENV
|
||||
else
|
||||
echo "SONARQUBE_SCANNER_PARAMS=$(jq -c . .github/workflows/sonarcloud/sonar-params.json)" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Begin analysis
|
||||
run: |
|
||||
dotnet-sonarscanner begin \
|
||||
/k:"$SONAR_PROJECT_KEY" \
|
||||
/o:"$SONAR_ORGANIZATION"
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore umbraco.sln
|
||||
|
||||
- name: Build solution
|
||||
run: GITHUB_ENV=/dev/null dotnet build umbraco.sln --no-restore -clp:ErrorsOnly # prevent sonar MSBuild integration from writing malformed values to $GITHUB_ENV
|
||||
|
||||
- name: Run unit tests with coverage
|
||||
run: |
|
||||
dotnet-coverage collect \
|
||||
"dotnet test tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj --no-build" \
|
||||
--output TestResults/coverage.xml \
|
||||
--output-format xml
|
||||
|
||||
- name: Upload analysis data
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: analysis-data
|
||||
include-hidden-files: true # required to capture the .sonarqube directory
|
||||
path: |
|
||||
.sonarqube/
|
||||
TestResults/
|
||||
retention-days: 1
|
||||
@@ -0,0 +1,98 @@
|
||||
name: "SonarQube Cloud - Upload"
|
||||
|
||||
# This workflow runs after the analysis workflow completes. It downloads the
|
||||
# Roslyn analysis output and coverage data produced by the analysis, then uploads
|
||||
# them to SonarCloud using the SONAR_TOKEN secret. Because it is triggered via
|
||||
# workflow_run, it always runs in the base repository context - even for fork
|
||||
# PRs - giving it safe access to secrets.
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["SonarQube Cloud - Analysis"]
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
env:
|
||||
SONAR_PROJECT_KEY: umbraco_Umbraco-CMS
|
||||
SONAR_ORGANIZATION: umbraco
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Upload analysis
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.workflow_run.conclusion == 'success' # only run if the build succeeded
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.workflow_run.head_sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download analysis data
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: analysis-data
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path: analysis-data
|
||||
|
||||
- name: Cache SonarQube packages
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
restore-keys: ${{ runner.os }}-sonar
|
||||
|
||||
- name: Install sonarscanner
|
||||
run: |
|
||||
dotnet tool install --global dotnet-sonarscanner
|
||||
|
||||
- name: Set analysis context
|
||||
env:
|
||||
IS_FORK: ${{ github.event.workflow_run.head_repository.full_name != github.repository }}
|
||||
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
||||
WORKFLOW_EVENT: ${{ github.event.workflow_run.event }}
|
||||
WR_PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }}
|
||||
WR_PR_HEAD_REF: ${{ github.event.workflow_run.pull_requests[0].head.ref }}
|
||||
WR_PR_BASE_REF: ${{ github.event.workflow_run.pull_requests[0].base.ref }}
|
||||
WR_HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||
run: |
|
||||
if [ "$IS_FORK" = "true" ]; then
|
||||
echo "SONARQUBE_SCANNER_PARAMS=$(git show origin/$DEFAULT_BRANCH:.github/workflows/sonarcloud/sonar-params.json | jq -c .)" | tee -a $GITHUB_ENV # read from default branch for fork PRs
|
||||
else
|
||||
echo "SONARQUBE_SCANNER_PARAMS=$(jq -c . .github/workflows/sonarcloud/sonar-params.json)" | tee -a $GITHUB_ENV
|
||||
fi
|
||||
if [ "$WORKFLOW_EVENT" = "pull_request" ]; then
|
||||
echo "PR_NUMBER=$WR_PR_NUMBER" | tee -a $GITHUB_ENV
|
||||
echo "PR_HEAD_REF=$WR_PR_HEAD_REF" | tee -a $GITHUB_ENV
|
||||
echo "PR_BASE_REF=$WR_PR_BASE_REF" | tee -a $GITHUB_ENV
|
||||
else
|
||||
echo "BRANCH_NAME=$WR_HEAD_BRANCH" | tee -a $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Begin analysis
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
run: |
|
||||
dotnet-sonarscanner begin \
|
||||
/k:"$SONAR_PROJECT_KEY" \
|
||||
/o:"$SONAR_ORGANIZATION" \
|
||||
/d:sonar.token="$SONAR_TOKEN" \
|
||||
${BRANCH_NAME:+/d:sonar.branch.name="$BRANCH_NAME"} \
|
||||
${PR_NUMBER:+/d:sonar.pullrequest.key="$PR_NUMBER"} \
|
||||
${PR_HEAD_REF:+/d:sonar.pullrequest.branch="$PR_HEAD_REF"} \
|
||||
${PR_BASE_REF:+/d:sonar.pullrequest.base="$PR_BASE_REF"}
|
||||
|
||||
- name: Restore analysis data from build
|
||||
run: |
|
||||
cp -r analysis-data/.sonarqube/out/. .sonarqube/out/
|
||||
cp -r analysis-data/.sonarqube/conf/[0-9]* .sonarqube/conf/
|
||||
cp -r analysis-data/TestResults TestResults
|
||||
|
||||
- name: End analysis
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
run: dotnet-sonarscanner end /d:sonar.token="$SONAR_TOKEN"
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"sonar.cs.vscoveragexml.reportsPaths": "TestResults/coverage.xml",
|
||||
"sonar.inclusions": "src/**,templates/**,tools/**,tests/**,.github/**,build/**",
|
||||
"sonar.exclusions": "**/bin/**,**/obj/**,**/node_modules/**,**/lang/*.ts,**/mocks/**,**/wwwroot/**,**/dist-cms/**,**/*.generated.cs,src/Umbraco.Web.UI/umbraco/**,src/Umbraco.Cms.Persistence.EFCore.*/Migrations/**,src/Umbraco.Web.UI.Client/src/packages/core/backend-api/**,**/.nuget/**",
|
||||
"sonar.test.inclusions": "tests/**,**/*.test.ts,**/*.spec.ts",
|
||||
"sonar.typescript.tsconfigPaths": "src/Umbraco.Web.UI.Client/tsconfig.json,src/Umbraco.Web.UI.Client/tsconfig.node.json,src/Umbraco.Web.UI.Login/tsconfig.json",
|
||||
"sonar.scanner.skipJreProvisioning": "true"
|
||||
}
|
||||
@@ -121,3 +121,6 @@ trace.zip
|
||||
/tests/Umbraco.Tests.Integration/umbraco-package-schema.json
|
||||
/src/Umbraco.Cms/appsettings-schema.json
|
||||
.playwright-mcp/
|
||||
|
||||
# SonarQube local analysis cache
|
||||
.sonarqube/
|
||||
|
||||
@@ -48,7 +48,6 @@ dotnet_analyzer_diagnostic.category-StyleCop.CSharp.OrderingRules.severity = sug
|
||||
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.MaintainabilityRules.severity = suggestion
|
||||
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.LayoutRules.severity = suggestion
|
||||
|
||||
dotnet_diagnostic.SA1636.severity = none # SA1636: File header copyright text should match
|
||||
dotnet_diagnostic.SA1101.severity = none # PrefixLocalCallsWithThis - stylecop appears to be ignoring dotnet_style_qualification_for_*
|
||||
dotnet_diagnostic.SA1309.severity = none # FieldNamesMustNotBeginWithUnderscore
|
||||
|
||||
|
||||
Reference in New Issue
Block a user