| Dmytro Shteflyuk | 3346c01 | 2026-02-13 17:40:41 -0500 | [diff] [blame] | 1 | name: "Static Code Analysis" |
| 2 | |
| 3 | on: |
| 4 | push: |
| 5 | branches: ["*"] |
| 6 | pull_request: |
| 7 | branches: ["*"] |
| 8 | workflow_dispatch: |
| 9 | |
| 10 | env: |
| 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 | |
| 36 | permissions: |
| 37 | contents: read |
| 38 | |
| 39 | jobs: |
| 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 |