11name : Frontend Code Quality Workflow
22
3- on : workflow_call
3+ on :
4+ workflow_call :
5+ secrets :
6+ SONAR_CLOUD_TOKEN :
7+ required : false
8+ inputs :
9+ SONAR_CLOUD_ORG :
10+ required : false
11+ description : " SonarCloud organization key, e.g., 'my-org'"
12+ type : string
13+ default : " collaborationfactory"
14+ SONAR_CLOUD_PROJECT_KEY :
15+ required : false
16+ description : " SonarCloud project key, e.g., 'my-project'"
17+ type : string
18+ default : " "
19+ SONAR_PROPERTIES :
20+ required : false
21+ description : " Additional sonar-project.properties content"
22+ type : string
23+ default : " "
24+ GITHUB_RUNNER :
25+ required : false
26+ description : " Github runner which is used to run sonar scan jobs"
27+ type : string
28+ default : ' medium'
429
530jobs :
631 code-quality :
934 matrix :
1035 target : [ 'test' ]
1136 jobIndex : [ 1, 2, 3,4 ]
37+ fail-fast : false # Continue running all matrix combinations even if one fails
1238 env :
1339 jobCount : 4
40+ coverageEnabled : ${{ secrets.SONAR_CLOUD_TOKEN != '' }}
1441 steps :
1542 - uses : actions/checkout@v4
1643 with :
1744 ref : ${{ github.event.pull_request.head.ref }}
1845 fetch-depth : 0
1946
20- - uses : actions/setup-node@v3
47+ - uses : actions/setup-node@v4
2148 with :
2249 node-version : 18.19.1
2350 - name : Cache Node Modules
@@ -33,11 +60,123 @@ jobs:
3360 - name : Linter
3461 run : npx nx affected --target=lint --parallel --configuration=dev --base=origin/${{ github.event.pull_request.base.ref }}
3562
63+ - name : Fetch base branch
64+ run : git fetch origin ${{ github.event.pull_request.base.ref }}:${{ github.event.pull_request.base.ref }} || true
65+
3666 - name : Unit Tests
67+ id : test
3768 uses : collaborationFactory/github-actions/.github/actions/run-many@release/25.2
69+ continue-on-error : true
3870 with :
3971 target : ${{ matrix.target }}
4072 jobIndex : ${{ matrix.jobIndex }}
4173 jobCount : ${{ env.jobCount }}
4274 base : ${{ github.event.pull_request.base.ref }}
4375 ref : ${{ github.event.pull_request.head.ref }}
76+
77+ - name : Upload Coverage Reports
78+ if : ${{ env.coverageEnabled == 'true' }}
79+ uses : actions/upload-artifact@v4
80+ with :
81+ name : coverage-${{ matrix.jobIndex }}
82+ path : coverage/
83+ retention-days : 7
84+
85+ - name : Fail pipeline if test step failed
86+ run : |
87+ if [ "${{ steps.test.outcome }}" = "failure" ]; then
88+ echo "Unit tests step failed, failing the pipeline."
89+ exit 1
90+ fi
91+
92+ check-sonar :
93+ name : Check SonarCloud Configuration
94+ runs-on : ubuntu-latest
95+ outputs :
96+ has_token : ${{ steps.check.outputs.has_token }}
97+ steps :
98+ - name : Check if SONAR_CLOUD_TOKEN exists
99+ id : check
100+ run : |
101+ if [ -n "${{ secrets.SONAR_CLOUD_TOKEN }}" ]; then
102+ echo "has_token=true" >> $GITHUB_OUTPUT
103+ echo "SonarCloud token is configured"
104+ else
105+ echo "has_token=false" >> $GITHUB_OUTPUT
106+ echo "SonarCloud token is not configured, skipping SonarCloud analysis"
107+ fi
108+
109+ sonar :
110+ name : SonarCloud
111+ needs : [code-quality, check-sonar]
112+ if : needs.check-sonar.outputs.has_token == 'true'
113+ runs-on : {{ inputs.GITHUB_RUNNER }}
114+ steps :
115+ - name : Set SonarCloud Project Key
116+ id : set-project-key
117+ run : |
118+ if [ -z "${{ inputs.SONAR_CLOUD_PROJECT_KEY }}" ]; then
119+ echo "project_key=collaborationFactory_${{ github.event.repository.name }}" >> $GITHUB_OUTPUT
120+ else
121+ echo "project_key=${{ inputs.SONAR_CLOUD_PROJECT_KEY }}" >> $GITHUB_OUTPUT
122+ fi
123+
124+ - name : Checkout
125+ uses : actions/checkout@v4
126+ with :
127+ fetch-depth : 0
128+
129+ - name : Write sonar-project.properties
130+ run : |
131+ echo "sonar.host.url=https://sonarcloud.io" > sonar-project.properties
132+ echo "sonar.organization=${{ inputs.SONAR_CLOUD_ORG }}" >> sonar-project.properties
133+ echo "sonar.projectKey=${{ steps.set-project-key.outputs.project_key }}" >> sonar-project.properties
134+ echo "sonar.test.inclusions=**/*.spec.ts,**/*.test.ts,**/*.spec.tsx,**/*.test.tsx" >> sonar-project.properties
135+ echo "sonar.javascript.lcov.reportPaths=./coverage/lcov.info" >> sonar-project.properties
136+ if [ -n "${{ inputs.SONAR_PROPERTIES }}" ]; then
137+ echo "${{ inputs.SONAR_PROPERTIES }}" >> sonar-project.properties
138+ fi
139+ cat sonar-project.properties
140+
141+ - name : download coverage report
142+ uses : actions/download-artifact@v4
143+ with :
144+ path : ./coverage
145+ pattern : coverage-*
146+ merge-multiple : true
147+
148+ - name : Check for LCOV files
149+ id : check-lcov
150+ run : |
151+ if find ./coverage -name "lcov*.info" -type f | grep -q .; then
152+ echo "lcov_exists=true" >> $GITHUB_OUTPUT
153+ echo "LCOV files found, will proceed with merge"
154+ else
155+ echo "lcov_exists=false" >> $GITHUB_OUTPUT
156+ echo "No LCOV files found, skipping merge"
157+ fi
158+
159+ - name : Merge matrix coverage reports
160+ if : steps.check-lcov.outputs.lcov_exists == 'true'
161+ run : |
162+ echo "=== Finding all LCOV files ==="
163+ find ./coverage -name "lcov*.info" -type f
164+ echo ""
165+ echo "=== Merging coverage files ==="
166+ npx lcov-result-merger "coverage/**/lcov*.info" coverage/lcov.info
167+ echo ""
168+ echo "=== Merge completed ==="
169+ ls -lh ./coverage/lcov.info
170+
171+ - name : Normalize LCOV paths for Linux
172+ if : steps.check-lcov.outputs.lcov_exists == 'true'
173+ run : |
174+ if [ -f "./coverage/lcov.info" ]; then
175+ sed -i 's|\\|/|g' ./coverage/lcov.info
176+ echo "Normalized LCOV paths to Unix format"
177+ fi
178+
179+ - name : SonarQube Scan
180+ uses : SonarSource/sonarqube-scan-action@v6
181+ env :
182+ SONAR_TOKEN : ${{ secrets.SONAR_CLOUD_TOKEN }}
0 commit comments