mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-27 17:27:43 +00:00
Add Sonar rule suppressions and update Publish workflow for Codacy rule sync
This commit is contained in:
+23
-2
@@ -29,11 +29,32 @@ resharper_condition_is_always_true_or_false_highlighting = hint
|
||||
resharper_access_to_modified_closure_highlighting = hint
|
||||
resharper_generic_enumerator_not_disposed_highlighting = hint
|
||||
|
||||
dotnet_diagnostic.S3776.severity = none
|
||||
# Sonar rule suppressions (synced from sonar-suppressions.json)
|
||||
# See sonar-suppressions.json for detailed justifications
|
||||
dotnet_diagnostic.S107.severity = none
|
||||
dotnet_diagnostic.S1144.severity = none
|
||||
dotnet_diagnostic.S1244.severity = none
|
||||
dotnet_diagnostic.S1944.severity = none
|
||||
dotnet_diagnostic.S2053.severity = none
|
||||
dotnet_diagnostic.S2245.severity = none
|
||||
dotnet_diagnostic.S2259.severity = none
|
||||
dotnet_diagnostic.S2583.severity = none
|
||||
dotnet_diagnostic.S2589.severity = none
|
||||
dotnet_diagnostic.S3236.severity = none
|
||||
dotnet_diagnostic.S3329.severity = none
|
||||
dotnet_diagnostic.S3604.severity = none
|
||||
dotnet_diagnostic.S3655.severity = none
|
||||
dotnet_diagnostic.S3776.severity = none
|
||||
dotnet_diagnostic.S3949.severity = none
|
||||
dotnet_diagnostic.S3966.severity = none
|
||||
dotnet_diagnostic.S4158.severity = none
|
||||
dotnet_diagnostic.S4347.severity = none
|
||||
dotnet_diagnostic.S5773.severity = none
|
||||
dotnet_diagnostic.S6781.severity = none
|
||||
|
||||
# Other suppressions
|
||||
dotnet_diagnostic.IDE0028.severity = none
|
||||
dotnet_diagnostic.CA1416.severity = none
|
||||
dotnet_diagnostic.S3236.severity = none
|
||||
dotnet_diagnostic.MA0046.severity = none
|
||||
dotnet_diagnostic.MA0003.severity = suggestion
|
||||
|
||||
|
||||
@@ -323,10 +323,42 @@ jobs:
|
||||
IFS=,
|
||||
echo "SONAR_SARIF_PATHS=${paths[*]}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Generate SonarCloud multicriteria from JSON
|
||||
id: sonar_rules
|
||||
run: |
|
||||
# Read rule IDs from sonar-suppressions.json and generate multicriteria args
|
||||
if [ ! -f "sonar-suppressions.json" ]; then
|
||||
echo "ERROR: sonar-suppressions.json not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract rule IDs and build multicriteria parameters
|
||||
rules=$(jq -r '.rules[].id' sonar-suppressions.json)
|
||||
multicriteria_names=""
|
||||
multicriteria_args=""
|
||||
index=1
|
||||
|
||||
for rule in $rules; do
|
||||
name="e${index}"
|
||||
if [ -n "$multicriteria_names" ]; then
|
||||
multicriteria_names="${multicriteria_names},${name}"
|
||||
else
|
||||
multicriteria_names="${name}"
|
||||
fi
|
||||
multicriteria_args="${multicriteria_args} /d:sonar.issue.ignore.multicriteria.${name}.ruleKey=csharpsquid:${rule} /d:sonar.issue.ignore.multicriteria.${name}.resourceKey=**/*"
|
||||
index=$((index + 1))
|
||||
done
|
||||
|
||||
echo "Generated multicriteria for $(echo "$rules" | wc -w) rules"
|
||||
echo "multicriteria_names=$multicriteria_names" >> "$GITHUB_OUTPUT"
|
||||
echo "multicriteria_args=$multicriteria_args" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Begin SonarCloud Analysis
|
||||
if: steps.check_sonar.outputs.skip != 'true'
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
MULTICRITERIA_NAMES: ${{ steps.sonar_rules.outputs.multicriteria_names }}
|
||||
MULTICRITERIA_ARGS: ${{ steps.sonar_rules.outputs.multicriteria_args }}
|
||||
run: |
|
||||
args=(
|
||||
"/k:mihakralj_QuanTAlib"
|
||||
@@ -339,11 +371,12 @@ jobs:
|
||||
"/d:sonar.exclusions=**/TestResults/**/*,**/bin/**/*,**/obj/**/*,**/*.html,**/coverage/**/*,**/CoverageReport/**/*,**/*.md,**/*.css,**/docs/**/*,**/archive/**/*,**/notebooks/**/*"
|
||||
"/d:sonar.test.exclusions=**Tests.cs,**/obj/**/*,**/bin/**/*"
|
||||
"/d:sonar.scanner.scanAll=false"
|
||||
"/d:sonar.issue.ignore.multicriteria=e1"
|
||||
"/d:sonar.issue.ignore.multicriteria.e1.ruleKey=csharpsquid:S107"
|
||||
"/d:sonar.issue.ignore.multicriteria.e1.resourceKey=**/*"
|
||||
"/d:sonar.issue.ignore.multicriteria=$MULTICRITERIA_NAMES"
|
||||
)
|
||||
|
||||
# Add the dynamically generated multicriteria args
|
||||
eval "args+=($MULTICRITERIA_ARGS)"
|
||||
|
||||
if [ -n "${SONAR_SARIF_PATHS:-}" ]; then
|
||||
args+=("/d:sonar.sarifReportPaths=$SONAR_SARIF_PATHS")
|
||||
fi
|
||||
@@ -670,10 +703,120 @@ jobs:
|
||||
fi
|
||||
|
||||
# ==============================================================================
|
||||
# 7) Codacy Upload (SARIF + Coverage)
|
||||
# 7) Codacy Rule Sync (disable Sonar rules from sonar-suppressions.json)
|
||||
# ==============================================================================
|
||||
Codacy_Rule_Sync:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ env.CHECKOUT_REF }}
|
||||
|
||||
- name: Check for Codacy API token
|
||||
id: check_api_token
|
||||
env:
|
||||
CODACY_API_TOKEN: ${{ secrets.CODACY_API_TOKEN }}
|
||||
run: |
|
||||
if [ -z "${CODACY_API_TOKEN:-}" ]; then
|
||||
echo "CODACY_API_TOKEN not set. Skipping Codacy rule sync."
|
||||
echo "To enable automatic rule sync, create a CODACY_API_TOKEN secret"
|
||||
echo "with an API token from https://app.codacy.com/account/apiTokens"
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Sync Sonar rules to Codacy
|
||||
if: steps.check_api_token.outputs.skip != 'true'
|
||||
env:
|
||||
CODACY_API_TOKEN: ${{ secrets.CODACY_API_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
PROVIDER="gh"
|
||||
ORGANIZATION="mihakralj"
|
||||
REPOSITORY="QuanTAlib"
|
||||
TOOL_UUID="8954dff3-f19c-429c-ac76-c45fa5e73b62" # SonarC# tool UUID
|
||||
API_BASE="https://app.codacy.com/api/v3"
|
||||
|
||||
if [ ! -f "sonar-suppressions.json" ]; then
|
||||
echo "ERROR: sonar-suppressions.json not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract rule IDs from JSON
|
||||
rules=$(jq -r '.rules[].id' sonar-suppressions.json)
|
||||
rule_count=$(echo "$rules" | wc -w)
|
||||
echo "Found $rule_count rules to disable in Codacy"
|
||||
|
||||
# Function to disable a pattern
|
||||
disable_pattern() {
|
||||
local pattern_id="$1"
|
||||
local response
|
||||
|
||||
echo "Disabling pattern: $pattern_id"
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X PATCH \
|
||||
"${API_BASE}/organizations/${PROVIDER}/${ORGANIZATION}/repositories/${REPOSITORY}/tools/${TOOL_UUID}/patterns/${pattern_id}" \
|
||||
-H "api-token: ${CODACY_API_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"enabled": false}')
|
||||
|
||||
http_code=$(echo "$response" | tail -1)
|
||||
body=$(echo "$response" | head -n -1)
|
||||
|
||||
if [ "$http_code" = "200" ] || [ "$http_code" = "204" ]; then
|
||||
echo " ✓ Successfully disabled $pattern_id"
|
||||
return 0
|
||||
elif [ "$http_code" = "404" ]; then
|
||||
echo " ⚠ Pattern $pattern_id not found (may not exist in this tool)"
|
||||
return 0
|
||||
else
|
||||
echo " ✗ Failed to disable $pattern_id (HTTP $http_code)"
|
||||
echo " Response: $body"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Process each rule
|
||||
success_count=0
|
||||
fail_count=0
|
||||
|
||||
for rule in $rules; do
|
||||
if disable_pattern "$rule"; then
|
||||
success_count=$((success_count + 1))
|
||||
else
|
||||
fail_count=$((fail_count + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== Codacy Rule Sync Summary ==="
|
||||
echo "Total rules: $rule_count"
|
||||
echo "Successful: $success_count"
|
||||
echo "Failed: $fail_count"
|
||||
|
||||
if [ $fail_count -gt 0 ]; then
|
||||
echo ""
|
||||
echo "Some rules failed to sync. This may be due to:"
|
||||
echo "- Pattern IDs not matching Codacy's internal naming"
|
||||
echo "- API rate limiting"
|
||||
echo "- Permission issues with the API token"
|
||||
echo ""
|
||||
echo "Consider manually verifying rules at:"
|
||||
echo "https://app.codacy.com/gh/${ORGANIZATION}/${REPOSITORY}/patterns"
|
||||
fi
|
||||
|
||||
# ==============================================================================
|
||||
# 8) Codacy Upload (SARIF + Coverage)
|
||||
# ==============================================================================
|
||||
Codacy_Upload:
|
||||
needs: [ReSharper_Analysis, Snyk_Scan, Semgrep_Scan, Sonar_Analysis]
|
||||
needs: [ReSharper_Analysis, Snyk_Scan, Semgrep_Scan, Sonar_Analysis, Codacy_Rule_Sync]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Single source of truth for Sonar rule suppressions across SonarCloud, Codacy, and local builds",
|
||||
"rules": [
|
||||
{
|
||||
"id": "S107",
|
||||
"reason": "Methods should not have too many parameters - high-performance SIMD methods require multiple parameters for zero-allocation patterns"
|
||||
},
|
||||
{
|
||||
"id": "S1144",
|
||||
"reason": "Unused private types or members should be removed - false positives on reflection/serialization patterns"
|
||||
},
|
||||
{
|
||||
"id": "S1244",
|
||||
"reason": "Floating point numbers should not be tested for equality - intentional exact comparisons in financial calculations"
|
||||
},
|
||||
{
|
||||
"id": "S1944",
|
||||
"reason": "Inappropriate casts should not be made - false positives on generic constraint patterns"
|
||||
},
|
||||
{
|
||||
"id": "S2053",
|
||||
"reason": "Hashes should include unpredictable salt - not applicable to non-cryptographic hashing"
|
||||
},
|
||||
{
|
||||
"id": "S2245",
|
||||
"reason": "Using pseudorandom number generators is security-sensitive - GBM uses deterministic seeds for reproducibility"
|
||||
},
|
||||
{
|
||||
"id": "S2259",
|
||||
"reason": "Null pointers should not be dereferenced - false positives with nullable reference types"
|
||||
},
|
||||
{
|
||||
"id": "S2583",
|
||||
"reason": "Conditionally executed code should be reachable - false positives on defensive programming patterns"
|
||||
},
|
||||
{
|
||||
"id": "S2589",
|
||||
"reason": "Boolean expressions should not be gratuitous - false positives on explicit clarity patterns"
|
||||
},
|
||||
{
|
||||
"id": "S3236",
|
||||
"reason": "Caller information arguments should not be provided explicitly - intentional for testing/debugging"
|
||||
},
|
||||
{
|
||||
"id": "S3329",
|
||||
"reason": "Cipher Block Chaining IVs should be unpredictable - not applicable to non-cryptographic code"
|
||||
},
|
||||
{
|
||||
"id": "S3604",
|
||||
"reason": "Member initializer values should not be redundant - null! is intentional for nullable reference types in Quantower adapters"
|
||||
},
|
||||
{
|
||||
"id": "S3655",
|
||||
"reason": "Empty nullable value should not be accessed - false positives with HasValue checks"
|
||||
},
|
||||
{
|
||||
"id": "S3776",
|
||||
"reason": "Cognitive Complexity of methods should not be too high - complex algorithms require complex implementations"
|
||||
},
|
||||
{
|
||||
"id": "S3949",
|
||||
"reason": "Calculations should not overflow - false positives on checked arithmetic contexts"
|
||||
},
|
||||
{
|
||||
"id": "S3966",
|
||||
"reason": "Objects should not be disposed more than once - false positives on defensive dispose patterns"
|
||||
},
|
||||
{
|
||||
"id": "S4158",
|
||||
"reason": "Empty collections should not be accessed or iterated - false positives on lazy initialization"
|
||||
},
|
||||
{
|
||||
"id": "S4347",
|
||||
"reason": "Secure random number generators should not output predictable values - deterministic seeds for reproducibility"
|
||||
},
|
||||
{
|
||||
"id": "S5773",
|
||||
"reason": "Types allowed to be deserialized should be restricted - not applicable to this codebase"
|
||||
},
|
||||
{
|
||||
"id": "S6781",
|
||||
"reason": "JWT tokens should not be created using insecure secrets - not applicable to this codebase"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user