@@ -10,38 +10,38 @@ concurrency:
1010 group : ci-release-${{ github.ref }}
1111 cancel-in-progress : true
1212
13+ env :
14+ SONAR_HOST_URL : " https://sonarcloud.io"
15+ SONAR_ORGANIZATION : " ciscode"
16+ SONAR_PROJECT_KEY : " CISCODE-MA_AuditKit"
17+ NODE_VERSION : " 22"
18+
19+ # ─── Job 1: Static checks (fast feedback, runs in parallel with test) ──────────
1320jobs :
14- ci :
21+ quality :
22+ name : Quality Checks
1523 runs-on : ubuntu-latest
24+ timeout-minutes : 10
1625
1726 permissions :
1827 contents : read
19- statuses : write
20- timeout-minutes : 25
21-
22- # Config stays in the workflow file (token stays in repo secrets)
23- env :
24- SONAR_HOST_URL : " https://sonarcloud.io"
25- SONAR_ORGANIZATION : " ciscode"
26- SONAR_PROJECT_KEY : " CISCODE-MA_AuditKit"
2728
2829 steps :
2930 - name : Checkout
3031 uses : actions/checkout@v4
31- with :
32- fetch-depth : 0
3332
3433 - name : Setup Node
3534 uses : actions/setup-node@v4
3635 with :
37- node-version : " 22 "
36+ node-version : ${{ env.NODE_VERSION }}
3837 cache : " npm"
3938
4039 - name : Install
4140 run : npm ci
4241
43- - name : Audit
44- run : npm audit --production
42+ - name : Security Audit
43+ # Only fail on high/critical — moderate noise in dev deps is expected
44+ run : npm audit --production --audit-level=high
4545
4646 - name : Format
4747 run : npm run format
@@ -52,12 +52,94 @@ jobs:
5252 - name : Lint
5353 run : npm run lint
5454
55+ # ─── Job 2: Tests + Coverage (artifact passed to Sonar) ────────────────────────
56+ test :
57+ name : Test & Coverage
58+ runs-on : ubuntu-latest
59+ timeout-minutes : 15
60+
61+ permissions :
62+ contents : read
63+
64+ steps :
65+ - name : Checkout
66+ uses : actions/checkout@v4
67+
68+ - name : Setup Node
69+ uses : actions/setup-node@v4
70+ with :
71+ node-version : ${{ env.NODE_VERSION }}
72+ cache : " npm"
73+
74+ - name : Install
75+ run : npm ci
76+
5577 - name : Test (with coverage)
5678 run : npm run test:cov
5779
80+ - name : Upload coverage report
81+ uses : actions/upload-artifact@v4
82+ with :
83+ name : coverage-report
84+ path : coverage/
85+ retention-days : 1
86+
87+ # ─── Job 3: Build ──────────────────────────────────────────────────────────────
88+ build :
89+ name : Build
90+ runs-on : ubuntu-latest
91+ needs : [quality, test]
92+ timeout-minutes : 10
93+
94+ permissions :
95+ contents : read
96+
97+ steps :
98+ - name : Checkout
99+ uses : actions/checkout@v4
100+
101+ - name : Setup Node
102+ uses : actions/setup-node@v4
103+ with :
104+ node-version : ${{ env.NODE_VERSION }}
105+ cache : " npm"
106+
107+ - name : Install
108+ run : npm ci
109+
58110 - name : Build
59111 run : npm run build
60112
113+ # ─── Job 4: SonarCloud (depends on test for coverage data) ─────────────────────
114+ sonar :
115+ name : SonarCloud Analysis
116+ runs-on : ubuntu-latest
117+ needs : [test]
118+ timeout-minutes : 15
119+
120+ permissions :
121+ contents : read
122+
123+ steps :
124+ - name : Checkout
125+ uses : actions/checkout@v4
126+ with :
127+ # Full history required for accurate blame & new code detection
128+ fetch-depth : 0
129+
130+ - name : Download coverage report
131+ uses : actions/download-artifact@v4
132+ with :
133+ name : coverage-report
134+ path : coverage/
135+
136+ - name : Cache SonarCloud packages
137+ uses : actions/cache@v4
138+ with :
139+ path : ~/.sonar/cache
140+ key : sonar-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
141+ restore-keys : sonar-${{ runner.os }}-
142+
61143 - name : SonarCloud Scan
62144 uses : SonarSource/sonarqube-scan-action@v6
63145 env :
@@ -69,24 +151,50 @@ jobs:
69151 -Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }}
70152 -Dsonar.sources=src
71153 -Dsonar.tests=test
154+ -Dsonar.test.inclusions=**/*.spec.ts,**/*.test.ts
155+ -Dsonar.exclusions=**/node_modules/**,**/dist/**,**/coverage/**,**/*.d.ts
156+ -Dsonar.coverage.exclusions=**/*.spec.ts,**/*.test.ts,**/index.ts
72157 -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
158+ -Dsonar.typescript.tsconfigPath=tsconfig.json
159+ -Dsonar.qualitygate.wait=true
160+ -Dsonar.qualitygate.timeout=300
73161
74- - name : SonarCloud Quality Gate
75- uses : SonarSource/sonarqube-quality-gate-action@v1
76- timeout-minutes : 10
77- env :
78- SONAR_TOKEN : ${{ secrets.SONAR_TOKEN }}
79- SONAR_HOST_URL : ${{ env.SONAR_HOST_URL }}
162+ # ─── Job 5: Final status report (always runs) ──────────────────────────────────
163+ report :
164+ name : Report CI Status
165+ runs-on : ubuntu-latest
166+ needs : [quality, test, build, sonar]
167+ # Run even if upstream jobs failed
168+ if : always()
169+ timeout-minutes : 5
170+
171+ permissions :
172+ contents : read
173+ statuses : write
80174
81- - name : Report CI status
82- if : always()
175+ steps :
176+ - name : Resolve overall result
177+ id : result
178+ run : |
179+ results="${{ needs.quality.result }} ${{ needs.test.result }} ${{ needs.build.result }} ${{ needs.sonar.result }}"
180+ if echo "$results" | grep -qE "failure|cancelled"; then
181+ echo "state=failure" >> $GITHUB_OUTPUT
182+ echo "desc=One or more CI checks failed" >> $GITHUB_OUTPUT
183+ else
184+ echo "state=success" >> $GITHUB_OUTPUT
185+ echo "desc=All CI checks passed" >> $GITHUB_OUTPUT
186+ fi
187+
188+ - name : Post commit status
83189 uses : actions/github-script@v7
84190 with :
85191 script : |
86192 await github.rest.repos.createCommitStatus({
87193 owner: context.repo.owner,
88194 repo: context.repo.repo,
89195 sha: context.sha,
90- state: '${{ job.status }}' === 'success' ? 'success' : 'failure',
91- description: 'CI checks completed'
196+ state: '${{ steps.result.outputs.state }}',
197+ context: 'CI / Release Check',
198+ description: '${{ steps.result.outputs.desc }}',
199+ target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`
92200 })
0 commit comments