Skip to content

Commit ea36907

Browse files
feat: add cppcheck static analysis (#170)
* feat: add cppcheck static analysis Added cppcheck static analysis integration: - Added cppcheck to Dockerfile - Created run_cppcheck.sh script - Added VSCode task for running cppcheck - Added GitHub Actions workflow for CI integration Link to Devin run: https://app.devin.ai/sessions/845a7fbce65a49a6b7894d63572911f4 --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Zachary J. Fields <zachary_fields@yahoo.com>
1 parent 3910cbd commit ea36907

6 files changed

Lines changed: 148 additions & 10 deletions

File tree

.github/actions/load-ci-image/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ runs:
66
uses: docker/setup-buildx-action@v2
77

88
- name: Download image artifact
9-
uses: actions/download-artifact@v3
9+
uses: actions/download-artifact@v4
1010
with:
1111
name: note_c_ci_image
1212
path: /tmp

.github/workflows/ci.yml

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414

1515
steps:
1616
- name: Checkout code
17-
uses: actions/checkout@v3
17+
uses: actions/checkout@v4
1818

1919
# TODO: This is a 3rd party GitHub action from some dude. Ideally, we'd
2020
# use something more "official".
@@ -34,7 +34,7 @@ jobs:
3434

3535
steps:
3636
- name: Checkout code
37-
uses: actions/checkout@v3
37+
uses: actions/checkout@v4
3838

3939
- name: Login to GitHub Container Registry
4040
uses: docker/login-action@v2
@@ -55,7 +55,7 @@ jobs:
5555
outputs: type=docker,dest=/tmp/note_c_ci_image.tar
5656

5757
- name: Upload image artifact
58-
uses: actions/upload-artifact@v3
58+
uses: actions/upload-artifact@v4
5959
with:
6060
name: note_c_ci_image
6161
path: /tmp/note_c_ci_image.tar
@@ -67,7 +67,7 @@ jobs:
6767

6868
steps:
6969
- name: Checkout code
70-
uses: actions/checkout@v3
70+
uses: actions/checkout@v4
7171

7272
- name: Load CI Docker image
7373
# Only load the Docker image artifact if build_ci_docker_image actually
@@ -86,7 +86,7 @@ jobs:
8686

8787
steps:
8888
- name: Checkout code
89-
uses: actions/checkout@v3
89+
uses: actions/checkout@v4
9090

9191
- name: Load CI Docker image
9292
# Only load the Docker image artifact if build_ci_docker_image actually
@@ -105,9 +105,11 @@ jobs:
105105

106106
steps:
107107
- name: Checkout code
108-
uses: actions/checkout@v3
108+
uses: actions/checkout@v4
109109

110110
- name: Load CI Docker image
111+
# Only load the Docker image artifact if build_ci_docker_image actually
112+
# ran (e.g. it wasn't skipped and was successful).
111113
if: ${{ needs.build_ci_docker_image.result == 'success' }}
112114
uses: ./.github/actions/load-ci-image
113115

@@ -131,9 +133,11 @@ jobs:
131133

132134
steps:
133135
- name: Checkout code
134-
uses: actions/checkout@v3
136+
uses: actions/checkout@v4
135137

136138
- name: Load CI Docker image
139+
# Only load the Docker image artifact if build_ci_docker_image actually
140+
# ran (e.g. it wasn't skipped and was successful).
137141
if: ${{ needs.build_ci_docker_image.result == 'success' }}
138142
uses: ./.github/actions/load-ci-image
139143

@@ -147,17 +151,38 @@ jobs:
147151
needs: [build_ci_docker_image]
148152

149153
steps:
150-
- name: Checkout Code
151-
uses: actions/checkout@v3
154+
- name: Checkout code
155+
uses: actions/checkout@v4
152156

153157
- name: Load CI Docker image
158+
# Only load the Docker image artifact if build_ci_docker_image actually
159+
# ran (e.g. it wasn't skipped and was successful).
154160
if: ${{ needs.build_ci_docker_image.result == 'success' }}
155161
uses: ./.github/actions/load-ci-image
156162

157163
- name: Run astyle
158164
run: |
159165
docker run --rm --volume $(pwd):/note-c/ --workdir /note-c/ --entrypoint ./scripts/run_astyle.sh ghcr.io/blues/note_c_ci:latest
160166
167+
run_cppcheck:
168+
runs-on: ubuntu-latest
169+
if: ${{ always() }}
170+
needs: [build_ci_docker_image]
171+
172+
steps:
173+
- name: Checkout code
174+
uses: actions/checkout@v4
175+
176+
- name: Load CI Docker image
177+
# Only load the Docker image artifact if build_ci_docker_image actually
178+
# ran (e.g. it wasn't skipped and was successful).
179+
if: ${{ needs.build_ci_docker_image.result == 'success' }}
180+
uses: ./.github/actions/load-ci-image
181+
182+
- name: Run cppcheck
183+
run: |
184+
docker run --rm --volume $(pwd):/note-c/ --workdir /note-c/ --entrypoint ./scripts/run_cppcheck.sh ghcr.io/blues/note_c_ci:latest
185+
161186
publish_ci_image:
162187
runs-on: ubuntu-latest
163188
# Make sure unit tests unit tests passed before publishing.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ __pycache__/
88
*.code-workspace
99
*.orig
1010
settings.json
11+
12+
# Development Artifacts
13+
cppcheck_output.txt

.vscode/tasks.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,21 @@
104104
"group": {
105105
"kind": "none"
106106
}
107+
},
108+
{
109+
"label": "Note-C: Run Static Analysis",
110+
"type": "shell",
111+
"command": "${workspaceFolder}/scripts/run_cppcheck.sh",
112+
"options": {
113+
"cwd": "${workspaceFolder}",
114+
"env": {
115+
"LC_ALL": "C"
116+
}
117+
},
118+
"problemMatcher": [
119+
"$gcc"
120+
],
121+
"group": "test"
107122
}
108123
]
109124
}

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ RUN ["dash", "-c", "\
6565
nano \
6666
python3-pip \
6767
python3-sphinx \
68+
cppcheck \
6869
valgrind \
6970
&& pip install --break-system-packages \
7071
breathe \

scripts/run_cppcheck.sh

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/bin/bash
2+
set -eo pipefail
3+
4+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
5+
SRC_DIR="$SCRIPT_DIR/.."
6+
7+
# Ensure build directory exists and compile_commands.json is generated
8+
if [ ! -d "$SRC_DIR/build" ] || [ ! -f "$SRC_DIR/build/compile_commands.json" ]; then
9+
cmake -B "$SRC_DIR/build" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
10+
fi
11+
12+
echo "Running Static Analysis..."
13+
echo
14+
15+
# Create a function to generate the summary
16+
generate_summary() {
17+
{
18+
# Initialize flag
19+
has_critical_issues=false
20+
21+
echo
22+
23+
# Always generate and display summary regardless of exit code
24+
echo "=== Static Analysis Summary ==="
25+
echo
26+
27+
# Display critical issues
28+
echo "Critical Issues (Errors & Warnings):"
29+
echo "-----------------------------------"
30+
grep -E "error:|warning:" cppcheck_output.txt | grep -v "Checking " | grep -v "nofile:0:" | \
31+
sort | uniq | awk -F': ' '{printf "%-40s %s\n", $1, $4}' || echo "None found"
32+
echo
33+
34+
# Display performance issues
35+
echo "Performance & Portability Issues:"
36+
echo "--------------------------------"
37+
grep -E "performance:|portability:" cppcheck_output.txt | grep -v "Checking " | \
38+
sort | uniq | awk -F': ' '{printf "%-40s %s\n", $1, $4}' || echo "None found"
39+
echo
40+
41+
# Count issues by severity
42+
echo " Issue Count by Severity: "
43+
echo "--------------------------"
44+
for sev in error warning performance portability style missingInclude information debug; do
45+
count=$(grep -c "${sev}:" cppcheck_output.txt) || true
46+
printf "%-15s %3d issues\n" "${sev^^}:" "$count"
47+
48+
# Check if 'sev' is 'error' or 'warning' and if 'count' is greater than 0
49+
if [[ "$sev" == "error" || "$sev" == "warning" ]] && [ "$count" -gt 0 ]; then
50+
has_critical_issues=true
51+
fi
52+
done
53+
echo
54+
55+
# Display status and details
56+
if [ $has_critical_issues ]; then
57+
echo "Status: FAILED - Critical issues found"
58+
echo
59+
echo "Review and fix critical issues before proceeding"
60+
else
61+
echo "Status: PASSED - No critical issues found"
62+
echo
63+
echo "Note: Review non-critical issues for potential improvements"
64+
fi
65+
}
66+
67+
# Return 1 if critical issues found, 0 otherwise
68+
if $has_critical_issues; then
69+
return 1
70+
else
71+
return 0
72+
fi
73+
}
74+
75+
# Run cppcheck and capture output and exit code
76+
cppcheck \
77+
--enable=all \
78+
--inconclusive \
79+
--std=c11 \
80+
--force \
81+
--inline-suppr \
82+
--suppress=unusedFunction \
83+
-i test \
84+
-i build/_deps \
85+
--template="{file}:{line}: {severity}: {id}: {message}" \
86+
--max-configs=32 \
87+
--check-library \
88+
--debug-warnings \
89+
. 2>&1 | tee cppcheck_output.txt
90+
91+
generate_summary
92+
93+
# Exit with cppcheck's status code
94+
exit $?

0 commit comments

Comments
 (0)