blob: 9fa2a167f56c650158072ee6b9b05766853c9572 [file] [log] [blame]
Dmytro Shteflyuk3346c012026-02-13 17:40:41 -05001name: "Static Code Analysis"
2
3on:
4 push:
5 branches: ["*"]
6 pull_request:
7 branches: ["*"]
8 workflow_dispatch:
9
10env:
11 BUILD_DEPS: automake bison flex git libboost-all-dev libevent-dev libssl-dev libtool make pkg-config
12 SCA_DEPS: cppcheck python3-flake8 sloccount libglib2.0-dev
13 # Disable all languages for which we don't have SCA checks in place
14 CONFIG_ARGS_FOR_SCA: >
15 --enable-tutorial=no
16 --disable-debug
17 --disable-tests
18 --disable-dependency-tracking
19 --without-java
20 --without-kotlin
21 --without-netstd
22 --without-nodejs
23 --without-nodets
24 --without-swift
25 --without-go
26 --without-dart
27 --without-erlang
28 --without-haxe
29 --without-ruby
30 --without-rs
31 --without-lua
32 --without-perl
33 --without-d
34 --without-cl
35
36permissions:
37 contents: read
38
39jobs:
40 sca:
41 runs-on: ubuntu-24.04
42 steps:
43 - uses: actions/checkout@v6
44
45 - name: Install dependencies
46 run: |
47 sudo apt-get update -yq
48 sudo apt-get install -y --no-install-recommends g++ $BUILD_DEPS $SCA_DEPS
49
50 - name: Set up PHP
51 uses: shivammathur/setup-php@v2
52 with:
53 # Lowest supported PHP version
54 php-version: "7.1"
55 extensions: mbstring, xml, curl, pcntl
56
57 - uses: actions/setup-python@v6
58 with:
59 python-version: "3.12"
60
61 - name: Install Python test dependencies
62 run: |
63 python -m pip install --upgrade pip setuptools wheel flake8
64
65 # Generate thrift files so the static code analysis includes an analysis
66 # of the files the thrift compiler spits out.
67 - name: Build compiler
68 id: compile
69 run: |
70 ./bootstrap.sh
71 ./configure $CONFIG_ARGS_FOR_SCA
72 make -j$(nproc) -C compiler/cpp
73
74 - name: Run cppcheck
75 id: cppcheck
76 continue-on-error: true
77 run: |
78 make -j$(nproc) -C lib/cpp
79 make -j$(nproc) -C test/cpp precross
80
81 make -j$(nproc) -C lib/c_glib
82 make -j$(nproc) -C test/c_glib precross
83
84 # Compiler cppcheck (All)
85 cppcheck --force --quiet --inline-suppr --enable=all -j2 compiler/cpp/src
86
87 # C++ cppcheck (All)
88 cppcheck --force --quiet --inline-suppr --enable=all -j2 lib/cpp/src lib/cpp/test test/cpp tutorial/cpp
89
90 # C Glib cppcheck (All)
91 cppcheck --force --quiet --inline-suppr --enable=all -j2 lib/c_glib/src lib/c_glib/test test/c_glib/src tutorial/c_glib
92
93 # Silent error checks
94 # See THRIFT-4371: flex generated scanner code causes false positives in cppcheck.
95 # suppress *:thrift/thriftl.cc -> flex-generated lexer triggers false null pointer paths.
96 # suppress syntaxError:thrift/thrifty.cc -> bison-generated parser is not fully parseable.
97 # suppress normalCheckLevelMaxBranches:compiler/cpp/src/* -> avoid info-only branch limit noise.
98 cppcheck --force --quiet --inline-suppr \
99 --suppress="*:thrift/thriftl.cc" \
100 --suppress="syntaxError:thrift/thrifty.cc" \
101 --suppress="normalCheckLevelMaxBranches:compiler/cpp/src/*" \
102 --error-exitcode=1 -j2 compiler/cpp/src
103
104 # suppress unknownMacro:lib/cpp/src/thrift/qt/* -> Qt namespace macro needs Qt preprocessing.
105 # suppress unknownMacro:lib/cpp/test/* -> Boost.Test macros are unresolved in standalone analysis.
106 # suppress syntaxError:lib/cpp/src/thrift/transport/TSSLSocket.cpp -> OpenSSL macro branches confuse parser.
107 # suppress normalCheckLevelMaxBranches:* -> avoid info-only branch limit noise.
108 # exclude lib/cpp/test/gen-cpp and test/cpp/gen-* -> generated fixtures duplicate source/test coverage.
109 cppcheck --force --quiet --inline-suppr \
110 --suppress="unknownMacro:lib/cpp/src/thrift/qt/*" \
111 --suppress="unknownMacro:lib/cpp/test/*" \
112 --suppress="syntaxError:lib/cpp/src/thrift/transport/TSSLSocket.cpp" \
113 --suppress="normalCheckLevelMaxBranches:lib/cpp/src/*" \
114 --suppress="normalCheckLevelMaxBranches:lib/cpp/test/*" \
115 --suppress="normalCheckLevelMaxBranches:test/cpp/*" \
116 --suppress="normalCheckLevelMaxBranches:tutorial/cpp/*" \
117 -i lib/cpp/test/gen-cpp \
118 -i test/cpp/gen-cpp \
119 -i test/cpp/gen-cpp-forward \
120 -i test/cpp/gen-cpp-private \
121 -i test/cpp/gen-cpp-enumclass \
122 --error-exitcode=1 -j2 lib/cpp/src lib/cpp/test test/cpp tutorial/cpp
123
124 # suppress unknownMacro:lib/c_glib/src/* -> GObject type macros are unresolved in standalone analysis.
125 # suppress unknownMacro:lib/c_glib/test/* -> test-side GLib macros are unresolved without full preprocess.
126 # suppress syntaxError:lib/c_glib/test/* -> GLib assert macros parse as syntax errors.
127 # suppress normalCheckLevelMaxBranches:* -> avoid info-only branch limit noise.
128 # exclude lib/c_glib/test/gen-c_glib -> generated bindings are covered by generator output checks.
129 # exclude lib/c_glib/test/gen-cpp -> generated skeleton has placeholder methods without returns.
130 cppcheck --force --quiet --inline-suppr \
131 --suppress="unknownMacro:lib/c_glib/src/*" \
132 --suppress="unknownMacro:lib/c_glib/test/*" \
133 --suppress="syntaxError:lib/c_glib/test/*" \
134 --suppress="normalCheckLevelMaxBranches:lib/c_glib/src/*" \
135 --suppress="normalCheckLevelMaxBranches:lib/c_glib/test/*" \
136 --suppress="normalCheckLevelMaxBranches:test/c_glib/*" \
137 --suppress="normalCheckLevelMaxBranches:tutorial/c_glib/*" \
138 -i lib/c_glib/test/gen-c_glib \
139 -i lib/c_glib/test/gen-cpp \
140 --error-exitcode=1 -j2 lib/c_glib/src lib/c_glib/test test/c_glib/src tutorial/c_glib
141
142 - name: Run flake8
143 id: flake8
144 continue-on-error: true
145 run: |
146 make -j$(nproc) -C test/py precross
147
148 flake8
149
150 - name: Run phpcs
151 id: phpcs
152 continue-on-error: true
153 run: |
154 # PHP code style
155 composer install --quiet
156 ./vendor/bin/phpcs
157
158 - name: Print statistics
159 if: ${{ always() }}
160 run: |
161 # TODO etc
162 echo "FIXMEs: $(grep -r FIXME * | wc -l)"
163 echo "HACKs: $(grep -r HACK * | wc -l)"
164 echo "TODOs: $(grep -r TODO * | wc -l)"
165
166 # LoC
167 sloccount .
168
169 # System info
170 # dpkg -l
171 uname -a
172
173 - name: Fail if any SCA check failed
174 if: ${{ always() && steps.compile.outcome == 'success' }}
175 env:
176 CPPCHECK_OUTCOME: ${{ steps.cppcheck.outcome }}
177 FLAKE8_OUTCOME: ${{ steps.flake8.outcome }}
178 PHPCS_OUTCOME: ${{ steps.phpcs.outcome }}
179 run: |
180 failed=0
181
182 if [ "$CPPCHECK_OUTCOME" != "success" ]; then
183 echo "::error::Step 'cppcheck' failed (outcome: $CPPCHECK_OUTCOME)"
184 failed=1
185 fi
186 if [ "$FLAKE8_OUTCOME" != "success" ]; then
187 echo "::error::Step 'flake8' failed (outcome: $FLAKE8_OUTCOME)"
188 failed=1
189 fi
190 if [ "$PHPCS_OUTCOME" != "success" ]; then
191 echo "::error::Step 'phpcs' failed (outcome: $PHPCS_OUTCOME)"
192 failed=1
193 fi
194
195 exit $failed