Skip to content

Commit 76a62cf

Browse files
committed
get codeql and code coverage working
Signed-off-by: Davanum Srinivas <[email protected]>
1 parent 0478308 commit 76a62cf

File tree

2 files changed

+254
-32
lines changed

2 files changed

+254
-32
lines changed

.github/workflows/code-scanning.yml

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,16 @@ jobs:
3232
prepare-environment:
3333
uses: ./.github/workflows/prepare-environment.yml
3434

35-
analyze:
36-
name: Analyze Go code with CodeQL
35+
codeql-pr-analysis:
36+
if: startsWith(github.ref, 'refs/heads/pull-request/')
37+
name: CodeQL PR Analysis
3738
runs-on: linux-amd64-cpu4
3839
timeout-minutes: 360
3940
needs: prepare-environment
4041
permissions:
4142
security-events: write
4243
packages: read
44+
pull-requests: write # Required for posting comments
4345
steps:
4446
- name: Checkout repository
4547
uses: actions/checkout@v4
@@ -57,16 +59,78 @@ jobs:
5759
shellcheck-version: ${{ needs.prepare-environment.outputs.shellcheck_version }}
5860

5961
- name: Initialize CodeQL
60-
uses: github/codeql-action/init@v3
62+
uses: github/codeql-action/init@v4
6163
with:
6264
languages: go
6365
build-mode: manual
6466
env:
6567
CODEQL_EXTRACTOR_GO_BUILD_TRACING: on
66-
- shell: bash
68+
69+
- name: Build with CodeQL
6770
run: |
6871
make build-all
72+
73+
- name: Perform CodeQL Analysis
74+
uses: github/codeql-action/analyze@v4
75+
with:
76+
category: "/language:go"
77+
78+
- name: Post CodeQL Results to PR
79+
if: startsWith(github.ref, 'refs/heads/pull-request/')
80+
run: |
81+
PR_NUM="${{ github.ref }}"
82+
PR_NUM="${PR_NUM##*/}"
83+
84+
ALERTS=$(gh api "repos/${{ github.repository }}/code-scanning/alerts?ref=${{ github.ref }}&state=open" --jq 'length')
85+
86+
if [[ "$ALERTS" -eq 0 ]]; then
87+
REPORT="## 🛡️ CodeQL Analysis\n✅ No security issues found"
88+
else
89+
REPORT="## 🛡️ CodeQL Analysis\n🚨 Found **$ALERTS** security alert(s)\n\n🔗 [View details](https://github.com/${{ github.repository }}/security/code-scanning?query=is%3Aopen+branch%3A${{ github.ref_name }})"
90+
fi
91+
92+
echo -e "$REPORT" | gh pr comment "$PR_NUM" --body-file=-
93+
env:
94+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
95+
96+
codeql-baseline-analysis:
97+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
98+
name: CodeQL Baseline Analysis
99+
runs-on: linux-amd64-cpu4
100+
timeout-minutes: 360
101+
needs: prepare-environment
102+
permissions:
103+
security-events: write
104+
packages: read
105+
steps:
106+
- name: Checkout repository
107+
uses: actions/checkout@v4
108+
109+
- name: Setup build environment
110+
uses: ./.github/actions/setup-build-env
111+
with:
112+
go-version: ${{ needs.prepare-environment.outputs.go_version }}
113+
python-version: ${{ needs.prepare-environment.outputs.python_version }}
114+
poetry-version: ${{ needs.prepare-environment.outputs.poetry_version }}
115+
golangci-lint-version: ${{ needs.prepare-environment.outputs.golangci_lint_version }}
116+
protobuf-version: ${{ needs.prepare-environment.outputs.protobuf_version }}
117+
protoc-gen-go-version: ${{ needs.prepare-environment.outputs.protoc_gen_go_version }}
118+
protoc-gen-go-grpc-version: ${{ needs.prepare-environment.outputs.protoc_gen_go_grpc_version }}
119+
shellcheck-version: ${{ needs.prepare-environment.outputs.shellcheck_version }}
120+
121+
- name: Initialize CodeQL
122+
uses: github/codeql-action/init@v4
123+
with:
124+
languages: go
125+
build-mode: manual
126+
env:
127+
CODEQL_EXTRACTOR_GO_BUILD_TRACING: on
128+
129+
- name: Build with CodeQL
130+
run: |
131+
make build-all
132+
69133
- name: Perform CodeQL Analysis
70-
uses: github/codeql-action/analyze@v3
134+
uses: github/codeql-action/analyze@v4
71135
with:
72136
category: "/language:go"

.github/workflows/lint-test.yml

Lines changed: 185 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -178,32 +178,58 @@ jobs:
178178
steps:
179179
- uses: actions/checkout@v4
180180

181+
- name: Setup Go
182+
uses: actions/setup-go@v5
183+
with:
184+
go-version: 'stable'
185+
181186
- name: Download all coverage artifacts
182187
uses: actions/download-artifact@v4
183188
with:
184189
pattern: "*-results"
185190
path: coverage-artifacts
186-
merge-multiple: true
191+
merge-multiple: false
187192

188193
- name: Consolidate coverage files
189194
run: |
195+
set -e
190196
echo "Consolidating coverage files from all components..."
191197
mkdir -p consolidated-coverage
192198
193-
# Find all coverage.txt files and merge them
199+
# Initialize consolidated coverage with mode line
200+
echo "mode: set" > consolidated-coverage/coverage.txt
201+
202+
# Find all coverage.txt files and merge them properly
194203
find coverage-artifacts -name "coverage.txt" -type f | while read -r file; do
195204
echo "Processing: $file"
196-
# Extract the mode line (first line) if it's the first file
197-
if [ ! -f consolidated-coverage/coverage.txt ]; then
198-
head -n 1 "$file" > consolidated-coverage/coverage.txt
205+
206+
# Validate file exists and is not empty
207+
if [[ ! -f "$file" || ! -s "$file" ]]; then
208+
echo "Warning: Skipping empty or missing file: $file"
209+
continue
210+
fi
211+
212+
# Validate coverage file format
213+
if ! head -n 1 "$file" | grep -q "^mode:"; then
214+
echo "Warning: Skipping file with invalid format (no mode line): $file"
215+
continue
199216
fi
200-
# Append coverage data (skip mode line)
201-
tail -n +2 "$file" >> consolidated-coverage/coverage.txt
217+
218+
# Extract coverage data (skip mode line) and validate each line
219+
tail -n +2 "$file" | while IFS= read -r line; do
220+
# Skip empty lines
221+
[[ -z "$line" ]] && continue
222+
223+
# Validate coverage line format: file.go:start.col,end.col numStmts count
224+
if [[ "$line" =~ ^[^:]+:[0-9]+\.[0-9]+,[0-9]+\.[0-9]+[[:space:]]+[0-9]+[[:space:]]+[0-9]+$ ]]; then
225+
echo "$line" >> consolidated-coverage/coverage.txt
226+
else
227+
echo "Warning: Skipping malformed coverage line: $line"
228+
fi
229+
done
202230
done
203231
204-
echo "Consolidated coverage file created:"
205-
ls -la consolidated-coverage/
206-
head -n 10 consolidated-coverage/coverage.txt
232+
echo "✅ Coverage consolidation completed successfully"
207233
208234
- name: Upload consolidated coverage
209235
uses: actions/upload-artifact@v4
@@ -212,14 +238,125 @@ jobs:
212238
path: consolidated-coverage/coverage.txt
213239
retention-days: 30
214240

215-
- name: Generate Go Coverage Report
216-
uses: fgrosse/go-coverage-report@8c1d1a09864211d258937b1b1a5b849f7e4f2682 # v1.2.0
241+
- name: Extract PR number from branch name
242+
id: pr-number
243+
run: |
244+
if [[ "${{ github.ref }}" =~ pull-request/([0-9]+) ]]; then
245+
echo "pr_number=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT
246+
else
247+
echo "pr_number=" >> $GITHUB_OUTPUT
248+
fi
249+
250+
- name: Install go-coverage-report CLI tool
251+
run: go install github.com/fgrosse/go-coverage-report/cmd/[email protected]
252+
253+
- name: Get changed files
254+
uses: tj-actions/changed-files@aa08304bd477b800d468db44fe10f6c61f7f7b11
255+
id: changed-files
217256
with:
218-
coverage-artifact-name: "consolidated-code-coverage"
219-
coverage-file-name: "coverage.txt"
220-
root-package: "github.com/NVIDIA/nvsentinel"
221-
skip-comment: false
222-
continue-on-error: true # Don't fail if baseline coverage doesn't exist yet
257+
write_output_files: true
258+
json: true
259+
files: "**.go"
260+
files_ignore: "vendor/**"
261+
output_dir: .github/outputs
262+
263+
- name: Generate Coverage Report with Fixed PR Number
264+
run: |
265+
set -e # Exit on error
266+
267+
# Current coverage is already uploaded in this workflow
268+
echo "Downloading current coverage..."
269+
if ! gh run download "${{ github.run_id }}" --name=consolidated-code-coverage --dir=/tmp/current-coverage; then
270+
echo "❌ Failed to download current coverage artifacts"
271+
exit 1
272+
fi
273+
274+
if [[ ! -f /tmp/current-coverage/coverage.txt ]]; then
275+
echo "❌ Current coverage file not found"
276+
exit 1
277+
fi
278+
279+
mv /tmp/current-coverage/coverage.txt .github/outputs/new-coverage.txt
280+
281+
# Download baseline coverage from main (failure here is acceptable)
282+
echo "Downloading baseline coverage..."
283+
LAST_SUCCESSFUL_RUN=$(gh run list --status=success --branch=main --workflow=lint-test.yml --event=push --json=databaseId --limit=1 -q '.[] | .databaseId')
284+
285+
if [[ -n "$LAST_SUCCESSFUL_RUN" ]]; then
286+
echo "Found baseline run: $LAST_SUCCESSFUL_RUN"
287+
if gh run download "$LAST_SUCCESSFUL_RUN" --name=consolidated-code-coverage --dir=/tmp/baseline-coverage 2>/dev/null; then
288+
if [[ -f /tmp/baseline-coverage/coverage.txt ]]; then
289+
echo "✅ Baseline coverage found"
290+
mv /tmp/baseline-coverage/coverage.txt .github/outputs/old-coverage.txt
291+
else
292+
echo "⚠️ Baseline coverage file not found in artifact"
293+
touch .github/outputs/old-coverage.txt # Create empty file
294+
fi
295+
else
296+
echo "⚠️ Failed to download baseline coverage (creating empty baseline)"
297+
touch .github/outputs/old-coverage.txt # Create empty file
298+
fi
299+
else
300+
echo "⚠️ No successful baseline run found (creating empty baseline)"
301+
touch .github/outputs/old-coverage.txt # Create empty file
302+
fi
303+
304+
# Generate the report using fgrosse's CLI tool (same format!)
305+
echo "Generating coverage report..."
306+
if ! go-coverage-report -root=github.com/NVIDIA/nvsentinel \
307+
.github/outputs/old-coverage.txt \
308+
.github/outputs/new-coverage.txt \
309+
.github/outputs/all_modified_files.json > coverage-report.md 2> coverage-report.err; then
310+
echo "❌ Failed to generate coverage report"
311+
exit 1
312+
fi
313+
314+
# Check if report is empty and why
315+
if [[ ! -f coverage-report.md || ! -s coverage-report.md ]]; then
316+
if grep -q "no changed files" coverage-report.err 2>/dev/null; then
317+
echo "ℹ️ No Go files changed - skipping coverage report"
318+
echo "## 📊 Coverage Report" > coverage-report.md
319+
echo "No Go files were modified in this PR, so no coverage analysis is needed." >> coverage-report.md
320+
else
321+
echo "❌ Coverage report is empty or missing for unknown reason"
322+
echo "Error output from go-coverage-report:"
323+
cat coverage-report.err || echo "No error output"
324+
exit 1
325+
fi
326+
fi
327+
328+
echo "✅ Coverage report generated successfully"
329+
330+
# Post comment using our correct PR number
331+
if [[ -n "${{ steps.pr-number.outputs.pr_number }}" ]]; then
332+
echo "Posting coverage comment to PR #${{ steps.pr-number.outputs.pr_number }}..."
333+
334+
# Check for existing coverage comment
335+
EXISTING_COMMENT=$(gh api "repos/${{ github.repository }}/issues/${{ steps.pr-number.outputs.pr_number }}/comments" \
336+
--jq '.[] | select(.user.login=="github-actions[bot]" and (.body | test("Coverage Report|Coverage Δ"))) | .id' \
337+
| head -1 2>/dev/null || echo "")
338+
339+
if [[ -n "$EXISTING_COMMENT" ]]; then
340+
echo "Updating existing comment $EXISTING_COMMENT..."
341+
if ! gh api "repos/${{ github.repository }}/issues/${{ steps.pr-number.outputs.pr_number }}/comments/$EXISTING_COMMENT" \
342+
--method PATCH --input coverage-report.md; then
343+
echo "❌ Failed to update existing coverage comment"
344+
exit 1
345+
fi
346+
else
347+
echo "Creating new comment..."
348+
if ! gh pr comment "${{ steps.pr-number.outputs.pr_number }}" --body-file=coverage-report.md; then
349+
echo "❌ Failed to create coverage comment"
350+
exit 1
351+
fi
352+
fi
353+
354+
echo "✅ Coverage comment posted successfully"
355+
else
356+
echo "⚠️ No PR number found, skipping comment"
357+
fi
358+
env:
359+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
223360

224361
consolidated-coverage-baseline:
225362
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
@@ -234,27 +371,48 @@ jobs:
234371
with:
235372
pattern: "*-results"
236373
path: coverage-artifacts
237-
merge-multiple: true
374+
merge-multiple: false
238375

239376
- name: Consolidate coverage files
240377
run: |
378+
set -e
241379
echo "Consolidating coverage files from all components for baseline..."
242380
mkdir -p consolidated-coverage
243381
244-
# Find all coverage.txt files and merge them
382+
# Initialize consolidated coverage with mode line
383+
echo "mode: set" > consolidated-coverage/coverage.txt
384+
385+
# Find all coverage.txt files and merge them properly
245386
find coverage-artifacts -name "coverage.txt" -type f | while read -r file; do
246387
echo "Processing: $file"
247-
# Extract the mode line (first line) if it's the first file
248-
if [ ! -f consolidated-coverage/coverage.txt ]; then
249-
head -n 1 "$file" > consolidated-coverage/coverage.txt
388+
389+
# Validate file exists and is not empty
390+
if [[ ! -f "$file" || ! -s "$file" ]]; then
391+
echo "Warning: Skipping empty or missing file: $file"
392+
continue
393+
fi
394+
395+
# Validate coverage file format
396+
if ! head -n 1 "$file" | grep -q "^mode:"; then
397+
echo "Warning: Skipping file with invalid format (no mode line): $file"
398+
continue
250399
fi
251-
# Append coverage data (skip mode line)
252-
tail -n +2 "$file" >> consolidated-coverage/coverage.txt
400+
401+
# Extract coverage data (skip mode line) and validate each line
402+
tail -n +2 "$file" | while IFS= read -r line; do
403+
# Skip empty lines
404+
[[ -z "$line" ]] && continue
405+
406+
# Validate coverage line format: file.go:start.col,end.col numStmts count
407+
if [[ "$line" =~ ^[^:]+:[0-9]+\.[0-9]+,[0-9]+\.[0-9]+[[:space:]]+[0-9]+[[:space:]]+[0-9]+$ ]]; then
408+
echo "$line" >> consolidated-coverage/coverage.txt
409+
else
410+
echo "Warning: Skipping malformed coverage line: $line"
411+
fi
412+
done
253413
done
254414
255-
echo "Baseline coverage file created:"
256-
ls -la consolidated-coverage/
257-
echo "Total coverage lines: $(wc -l < consolidated-coverage/coverage.txt)"
415+
echo "✅ Coverage consolidation completed successfully"
258416
259417
- name: Upload consolidated coverage baseline
260418
uses: actions/upload-artifact@v4

0 commit comments

Comments
 (0)