Skip to content

Commit ef80994

Browse files
committed
Add visuals and metadata for Code Quality Report
- Created header bar visual for the Quality Overview page. - Added header title visual displaying "Code Quality - Findings by Repository". - Implemented severity slicer visual for filtering findings by severity. - Developed stacked bar chart visual to display code quality findings by repository and severity. - Introduced details table visual for displaying comprehensive findings data. - Established pages metadata for the Quality Overview page. - Added English culture metadata for semantic model. - Created multiple local date tables for enhanced date handling in the semantic model.
1 parent ee91e4a commit ef80994

53 files changed

Lines changed: 2935 additions & 1141 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.azuredevops/pipelines/code-quality-lint-gate.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ steps:
1212
- checkout: self
1313

1414
- script: |
15-
npx mega-linter-runner --flavor dotnetweb -e VALIDATE_ALL_CODEBASE=false -e SARIF_REPORTER=true
15+
npx mega-linter-runner -e VALIDATE_ALL_CODEBASE=false -e SARIF_REPORTER=true
1616
displayName: 'Run MegaLinter (changed files only)'
1717
1818
- script: |

.azuredevops/pipelines/code-quality-scan.yml

Lines changed: 25 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -13,55 +13,28 @@ pool:
1313
variables:
1414
- template: variables/common.yml
1515

16-
strategy:
17-
matrix:
18-
app001:
19-
appId: '001'
20-
repoName: 'cq-demo-app-001'
21-
app002:
22-
appId: '002'
23-
repoName: 'cq-demo-app-002'
24-
app003:
25-
appId: '003'
26-
repoName: 'cq-demo-app-003'
27-
app004:
28-
appId: '004'
29-
repoName: 'cq-demo-app-004'
30-
app005:
31-
appId: '005'
32-
repoName: 'cq-demo-app-005'
33-
34-
steps:
35-
- checkout: self
36-
37-
- task: UsePythonVersion@0
38-
inputs:
39-
versionSpec: '3.12'
40-
41-
- script: |
42-
pip install lizard
43-
pip install jscpd
44-
displayName: 'Install scanning tools'
45-
46-
- script: |
47-
npx mega-linter-runner --flavor dotnetweb -e VALIDATE_ALL_CODEBASE=true -e SARIF_REPORTER=true
48-
displayName: 'Run MegaLinter on $(repoName)'
49-
workingDirectory: $(repoName)
50-
51-
- script: |
52-
lizard --csv $(repoName)/src/ > $(Build.ArtifactStagingDirectory)/lizard-output.csv
53-
python src/converters/lizard-to-sarif.py \
54-
--input $(Build.ArtifactStagingDirectory)/lizard-output.csv \
55-
--output $(Build.ArtifactStagingDirectory)/complexity-$(appId).sarif
56-
displayName: 'Run complexity analysis on $(repoName)'
57-
58-
- task: AdvancedSecurity-Publish@1
59-
inputs:
60-
SarifFileDirectory: '$(Build.ArtifactStagingDirectory)'
61-
displayName: 'Publish SARIF to Advanced Security'
62-
63-
- task: PublishBuildArtifacts@1
64-
inputs:
65-
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
66-
ArtifactName: 'sarif-results-$(appId)'
67-
displayName: 'Publish SARIF artifacts'
16+
# This orchestrator triggers the per-app scan pipelines.
17+
# Each app pipeline runs in its own repo context so that
18+
# AdvancedSecurity-Publish@1 associates SARIF with the correct repo.
19+
20+
jobs:
21+
- job: TriggerScans
22+
displayName: 'Trigger per-repo code quality scans'
23+
steps:
24+
- checkout: none
25+
26+
- script: |
27+
set -euo pipefail
28+
for APP in 001 002 003 004 005; do
29+
PIPELINE_NAME="Code Quality Scan - cq-demo-app-${APP}"
30+
echo "Triggering pipeline: ${PIPELINE_NAME}"
31+
az pipelines run \
32+
--name "${PIPELINE_NAME}" \
33+
--branch main \
34+
--org "https://dev.azure.com/$(adoOrg)" \
35+
--project "$(adoProject)" \
36+
--output table || echo "⚠️ Failed to trigger ${PIPELINE_NAME} — it may not exist yet."
37+
done
38+
displayName: 'Trigger all app scan pipelines'
39+
env:
40+
AZURE_DEVOPS_EXT_PAT: $(System.AccessToken)

.azuredevops/pipelines/deploy-all.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,51 @@ variables:
77
- template: variables/common.yml
88

99
stages:
10+
- stage: DeployStorage
11+
displayName: 'Deploy ADLS Gen2 Storage'
12+
jobs:
13+
- job: DeployADLS
14+
displayName: 'Deploy ADLS Gen2 for scan results'
15+
steps:
16+
- checkout: self
17+
18+
- task: AzureCLI@2
19+
displayName: 'Create Storage Resource Group'
20+
inputs:
21+
azureSubscription: '$(serviceConnection)'
22+
scriptType: 'bash'
23+
scriptLocation: 'inlineScript'
24+
inlineScript: |
25+
az group create --name $(storageResourceGroup) --location $(location)
26+
27+
- task: AzureCLI@2
28+
displayName: 'Deploy ADLS Gen2 (uniqueString naming)'
29+
name: storage
30+
inputs:
31+
azureSubscription: '$(serviceConnection)'
32+
scriptType: 'bash'
33+
scriptLocation: 'inlineScript'
34+
inlineScript: |
35+
outputs=$(az deployment group create \
36+
--resource-group $(storageResourceGroup) \
37+
--name "deploy-storage-$(Build.BuildId)" \
38+
--template-file infra/storage.bicep \
39+
--query 'properties.outputs' -o json)
40+
STORAGE_NAME=$(echo $outputs | jq -r '.storageAccountName.value')
41+
CONTAINER=$(echo $outputs | jq -r '.containerName.value')
42+
DFS_ENDPOINT=$(echo $outputs | jq -r '.dfsEndpoint.value')
43+
echo "##vso[task.setvariable variable=storageAccountName;isoutput=true]$STORAGE_NAME"
44+
echo "##vso[task.setvariable variable=containerName;isoutput=true]$CONTAINER"
45+
echo "##vso[task.setvariable variable=dfsEndpoint;isoutput=true]$DFS_ENDPOINT"
46+
echo "Storage Account: $STORAGE_NAME"
47+
echo "Container: $CONTAINER"
48+
echo "DFS Endpoint: $DFS_ENDPOINT"
49+
1050
- stage: Deploy
1151
displayName: 'Deploy All Demo Apps'
52+
dependsOn: DeployStorage
53+
variables:
54+
storageAccountName: $[ stageDependencies.DeployStorage.DeployADLS.outputs['storage.storageAccountName'] ]
1255
jobs:
1356
- job: DeployApp001
1457
displayName: 'Deploy cq-demo-app-001'

.azuredevops/pipelines/scan-and-store.yml

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ pool:
1313
variables:
1414
- template: variables/common.yml
1515

16+
# Scan and Store orchestrator: triggers per-app scan pipelines then
17+
# collects and uploads SARIF artifacts to ADLS Gen2 for Power BI.
18+
1619
strategy:
1720
matrix:
1821
app001:
@@ -34,6 +37,20 @@ strategy:
3437
steps:
3538
- checkout: self
3639

40+
- task: AzureCLI@2
41+
displayName: 'Resolve ADLS Gen2 storage account name'
42+
name: resolveStorage
43+
inputs:
44+
azureSubscription: '$(serviceConnection)'
45+
scriptType: 'bash'
46+
scriptLocation: 'inlineScript'
47+
inlineScript: |
48+
STORAGE_NAME=$(az storage account list \
49+
--resource-group $(storageResourceGroup) \
50+
--query "[0].name" -o tsv)
51+
echo "Resolved storage account: $STORAGE_NAME"
52+
echo "##vso[task.setvariable variable=storageAccountName]$STORAGE_NAME"
53+
3754
- task: UsePythonVersion@0
3855
inputs:
3956
versionSpec: '3.12'
@@ -42,17 +59,32 @@ steps:
4259
pip install lizard
4360
displayName: 'Install scanning tools'
4461
62+
# Free disk space — the MegaLinter Docker image is large
4563
- script: |
46-
npx mega-linter-runner --flavor dotnetweb -e VALIDATE_ALL_CODEBASE=true -e SARIF_REPORTER=true
64+
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/boost
65+
sudo docker image prune -af
66+
df -h /
67+
displayName: 'Free disk space'
68+
69+
- script: |
70+
npx mega-linter-runner -e VALIDATE_ALL_CODEBASE=true -e SARIF_REPORTER=true
4771
displayName: 'Run MegaLinter on $(repoName)'
4872
workingDirectory: $(repoName)
73+
continueOnError: true
74+
75+
- script: |
76+
cp $(repoName)/megalinter-reports/sarif/*.sarif \
77+
$(Build.ArtifactStagingDirectory)/ 2>/dev/null || true
78+
displayName: 'Collect MegaLinter SARIF'
79+
continueOnError: true
4980
5081
- script: |
51-
lizard --csv $(repoName)/src/ > $(Build.ArtifactStagingDirectory)/lizard-output.csv
82+
lizard --csv $(repoName)/src/ > $(Build.ArtifactStagingDirectory)/lizard-output.csv 2>/dev/null || true
5283
python src/converters/lizard-to-sarif.py \
5384
--input $(Build.ArtifactStagingDirectory)/lizard-output.csv \
5485
--output $(Build.ArtifactStagingDirectory)/complexity-$(appId).sarif
5586
displayName: 'Run complexity analysis'
87+
continueOnError: true
5688
5789
- task: AzureCLI@2
5890
inputs:
@@ -66,7 +98,16 @@ steps:
6698
-AppId $(appId)
6799
displayName: 'Upload SARIF results to ADLS Gen2'
68100

69-
- task: AdvancedSecurity-Publish@1
70-
inputs:
71-
SarifFileDirectory: '$(Build.ArtifactStagingDirectory)'
72-
displayName: 'Publish SARIF to Advanced Security'
101+
- script: |
102+
set -euo pipefail
103+
PIPELINE_NAME="Code Quality Scan - $(repoName)"
104+
echo "Triggering pipeline: ${PIPELINE_NAME}"
105+
az pipelines run \
106+
--name "${PIPELINE_NAME}" \
107+
--branch main \
108+
--org "https://dev.azure.com/$(adoOrg)" \
109+
--project "$(adoProject)" \
110+
--output table || echo "⚠️ Failed to trigger ${PIPELINE_NAME}"
111+
displayName: 'Trigger per-app scan for Advanced Security'
112+
env:
113+
AZURE_DEVOPS_EXT_PAT: $(System.AccessToken)
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Code Quality Scan pipeline for individual app repos.
2+
# This pipeline runs in the context of the app repo so that
3+
# AdvancedSecurity-Publish@1 associates SARIF with this repo.
4+
#
5+
# Triggered manually or by the orchestrator pipeline in the scanner repo.
6+
#
7+
# Set pipeline variable 'megalinterFlavor' to the MegaLinter flavor for this
8+
# app's language (e.g. javascript, python, dotnet, java, go). Falls back to
9+
# the cupcake flavor if not set.
10+
11+
trigger: none
12+
13+
pool:
14+
vmImage: 'ubuntu-latest'
15+
16+
resources:
17+
repositories:
18+
- repository: scanner
19+
type: git
20+
name: CodeQuality/code-quality-scan-demo-app
21+
ref: main
22+
23+
steps:
24+
- checkout: self
25+
path: 'app'
26+
27+
- checkout: scanner
28+
path: 'scanner'
29+
30+
- task: UsePythonVersion@0
31+
inputs:
32+
versionSpec: '3.12'
33+
34+
# Free disk space — the MegaLinter Docker image is large
35+
- script: |
36+
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/boost
37+
sudo docker image prune -af
38+
df -h /
39+
displayName: 'Free disk space'
40+
41+
- script: |
42+
pip install lizard
43+
displayName: 'Install scanning tools'
44+
45+
- script: |
46+
npx mega-linter-runner --flavor $(megalinterFlavor) \
47+
-e VALIDATE_ALL_CODEBASE=true \
48+
-e SARIF_REPORTER=true
49+
displayName: 'Run MegaLinter ($(megalinterFlavor))'
50+
workingDirectory: '$(Pipeline.Workspace)/app'
51+
continueOnError: true
52+
53+
- script: |
54+
mkdir -p $(Build.ArtifactStagingDirectory)
55+
# Copy MegaLinter SARIF and fix paths from Docker container (/tmp/lint/)
56+
# to be relative to the repo root so Scans tab links work
57+
for f in $(Pipeline.Workspace)/app/megalinter-reports/sarif/*.sarif; do
58+
[ -f "$f" ] || continue
59+
sed 's|/tmp/lint/||g' "$f" > $(Build.ArtifactStagingDirectory)/$(basename "$f")
60+
done
61+
displayName: 'Collect MegaLinter SARIF'
62+
continueOnError: true
63+
64+
- script: |
65+
SRC_DIR="$(Pipeline.Workspace)/app/src"
66+
if [ ! -d "$SRC_DIR" ]; then
67+
SRC_DIR="$(Pipeline.Workspace)/app"
68+
fi
69+
lizard --csv "$SRC_DIR" \
70+
> $(Build.ArtifactStagingDirectory)/lizard-output.csv 2>/dev/null || true
71+
# Fix absolute paths in lizard CSV to be repo-relative before conversion
72+
sed -i "s|$(Pipeline.Workspace)/app/||g" \
73+
$(Build.ArtifactStagingDirectory)/lizard-output.csv 2>/dev/null || true
74+
python $(Pipeline.Workspace)/scanner/src/converters/lizard-to-sarif.py \
75+
--input $(Build.ArtifactStagingDirectory)/lizard-output.csv \
76+
--output $(Build.ArtifactStagingDirectory)/complexity.sarif
77+
displayName: 'Run complexity analysis'
78+
continueOnError: true
79+
80+
# Fix all SARIF uris to be repo-relative (strip any remaining absolute paths)
81+
- script: |
82+
cd $(Build.ArtifactStagingDirectory)
83+
for f in *.sarif; do
84+
[ -f "$f" ] || continue
85+
# Remove any absolute path prefix, keep repo-relative paths
86+
sed -i "s|$(Pipeline.Workspace)/app/||g" "$f"
87+
sed -i 's|/home/vsts/work/[^/]*/s/[^/]*/||g' "$f"
88+
done
89+
displayName: 'Normalize SARIF paths'
90+
continueOnError: true
91+
92+
# Copy SARIF to the directory AdvancedSecurity-Publish@1 actually reads from
93+
- script: |
94+
ADVSEC_DIR="$(Agent.TempDirectory)/.advsec"
95+
mkdir -p "$ADVSEC_DIR"
96+
cp $(Build.ArtifactStagingDirectory)/*.sarif "$ADVSEC_DIR/" 2>/dev/null || true
97+
echo "SARIF files in $ADVSEC_DIR:"
98+
ls -la "$ADVSEC_DIR/"*.sarif 2>/dev/null || echo " (none)"
99+
displayName: 'Stage SARIF for Advanced Security'
100+
condition: always()
101+
102+
- task: AdvancedSecurity-Publish@1
103+
displayName: 'Publish SARIF to Advanced Security'
104+
condition: always()
105+
106+
- task: PublishBuildArtifacts@1
107+
inputs:
108+
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
109+
ArtifactName: 'CodeAnalysisLogs'
110+
displayName: 'Publish SARIF artifacts'
111+
condition: always()

0 commit comments

Comments
 (0)