name: "Static Code Analysis" on: push: branches: ["*"] pull_request: branches: ["*"] workflow_dispatch: env: BUILD_DEPS: automake bison flex git libboost-all-dev libevent-dev libssl-dev libtool make pkg-config SCA_DEPS: cppcheck python3-flake8 sloccount libglib2.0-dev # Disable all languages for which we don't have SCA checks in place CONFIG_ARGS_FOR_SCA: > --enable-tutorial=no --disable-debug --disable-tests --disable-dependency-tracking --without-java --without-kotlin --without-netstd --without-nodejs --without-nodets --without-swift --without-go --without-dart --without-erlang --without-haxe --without-ruby --without-rs --without-lua --without-perl --without-d --without-cl permissions: contents: read jobs: sca: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 - name: Install dependencies run: | sudo apt-get update -yq sudo apt-get install -y --no-install-recommends g++ $BUILD_DEPS $SCA_DEPS - name: Set up PHP uses: shivammathur/setup-php@v2 with: # Lowest supported PHP version php-version: "7.1" extensions: mbstring, xml, curl, pcntl - uses: actions/setup-python@v6 with: python-version: "3.12" - name: Install Python test dependencies run: | python -m pip install --upgrade pip setuptools wheel flake8 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: "2.7" bundler-cache: true working-directory: "lib/rb" # Generate thrift files so the static code analysis includes an analysis # of the files the thrift compiler spits out. - name: Build compiler id: compile run: | ./bootstrap.sh ./configure $CONFIG_ARGS_FOR_SCA make -j$(nproc) -C compiler/cpp - name: Run cppcheck id: cppcheck continue-on-error: true run: | make -j$(nproc) -C lib/cpp make -j$(nproc) -C test/cpp precross make -j$(nproc) -C lib/c_glib make -j$(nproc) -C test/c_glib precross # Compiler cppcheck (All) cppcheck --force --quiet --inline-suppr --enable=all -j2 compiler/cpp/src # C++ cppcheck (All) cppcheck --force --quiet --inline-suppr --enable=all -j2 lib/cpp/src lib/cpp/test test/cpp tutorial/cpp # C Glib cppcheck (All) cppcheck --force --quiet --inline-suppr --enable=all -j2 lib/c_glib/src lib/c_glib/test test/c_glib/src tutorial/c_glib # Silent error checks # See THRIFT-4371: flex generated scanner code causes false positives in cppcheck. # suppress *:thrift/thriftl.cc -> flex-generated lexer triggers false null pointer paths. # suppress syntaxError:thrift/thrifty.cc -> bison-generated parser is not fully parseable. # suppress normalCheckLevelMaxBranches:compiler/cpp/src/* -> avoid info-only branch limit noise. cppcheck --force --quiet --inline-suppr \ --suppress="*:thrift/thriftl.cc" \ --suppress="syntaxError:thrift/thrifty.cc" \ --suppress="normalCheckLevelMaxBranches:compiler/cpp/src/*" \ --error-exitcode=1 -j2 compiler/cpp/src # suppress unknownMacro:lib/cpp/src/thrift/qt/* -> Qt namespace macro needs Qt preprocessing. # suppress unknownMacro:lib/cpp/test/* -> Boost.Test macros are unresolved in standalone analysis. # suppress syntaxError:lib/cpp/src/thrift/transport/TSSLSocket.cpp -> OpenSSL macro branches confuse parser. # suppress normalCheckLevelMaxBranches:* -> avoid info-only branch limit noise. # exclude lib/cpp/test/gen-cpp and test/cpp/gen-* -> generated fixtures duplicate source/test coverage. cppcheck --force --quiet --inline-suppr \ --suppress="unknownMacro:lib/cpp/src/thrift/qt/*" \ --suppress="unknownMacro:lib/cpp/test/*" \ --suppress="syntaxError:lib/cpp/src/thrift/transport/TSSLSocket.cpp" \ --suppress="normalCheckLevelMaxBranches:lib/cpp/src/*" \ --suppress="normalCheckLevelMaxBranches:lib/cpp/test/*" \ --suppress="normalCheckLevelMaxBranches:test/cpp/*" \ --suppress="normalCheckLevelMaxBranches:tutorial/cpp/*" \ -i lib/cpp/test/gen-cpp \ -i test/cpp/gen-cpp \ -i test/cpp/gen-cpp-forward \ -i test/cpp/gen-cpp-private \ -i test/cpp/gen-cpp-enumclass \ --error-exitcode=1 -j2 lib/cpp/src lib/cpp/test test/cpp tutorial/cpp # suppress unknownMacro:lib/c_glib/src/* -> GObject type macros are unresolved in standalone analysis. # suppress unknownMacro:lib/c_glib/test/* -> test-side GLib macros are unresolved without full preprocess. # suppress syntaxError:lib/c_glib/test/* -> GLib assert macros parse as syntax errors. # suppress normalCheckLevelMaxBranches:* -> avoid info-only branch limit noise. # exclude lib/c_glib/test/gen-c_glib -> generated bindings are covered by generator output checks. # exclude lib/c_glib/test/gen-cpp -> generated skeleton has placeholder methods without returns. cppcheck --force --quiet --inline-suppr \ --suppress="unknownMacro:lib/c_glib/src/*" \ --suppress="unknownMacro:lib/c_glib/test/*" \ --suppress="syntaxError:lib/c_glib/test/*" \ --suppress="normalCheckLevelMaxBranches:lib/c_glib/src/*" \ --suppress="normalCheckLevelMaxBranches:lib/c_glib/test/*" \ --suppress="normalCheckLevelMaxBranches:test/c_glib/*" \ --suppress="normalCheckLevelMaxBranches:tutorial/c_glib/*" \ -i lib/c_glib/test/gen-c_glib \ -i lib/c_glib/test/gen-cpp \ --error-exitcode=1 -j2 lib/c_glib/src lib/c_glib/test test/c_glib/src tutorial/c_glib - name: Run flake8 id: flake8 continue-on-error: true run: | make -j$(nproc) -C test/py precross flake8 - name: Run phpcs id: phpcs continue-on-error: true run: | # PHP code style composer install --quiet ./vendor/bin/phpcs - name: Run rubocop id: rubocop continue-on-error: true working-directory: "lib/rb" run: | bundle exec rubocop --config .rubocop.yml --format progress --format github . ../../test/rb ../../tutorial/rb - name: Print statistics if: ${{ always() }} run: | # TODO etc echo "FIXMEs: $(grep -r FIXME * | wc -l)" echo "HACKs: $(grep -r HACK * | wc -l)" echo "TODOs: $(grep -r TODO * | wc -l)" # LoC sloccount . # System info # dpkg -l uname -a - name: Fail if any SCA check failed if: ${{ always() && steps.compile.outcome == 'success' }} env: CPPCHECK_OUTCOME: ${{ steps.cppcheck.outcome }} FLAKE8_OUTCOME: ${{ steps.flake8.outcome }} PHPCS_OUTCOME: ${{ steps.phpcs.outcome }} RUBOCOP_OUTCOME: ${{ steps.rubocop.outcome }} run: | failed=0 if [ "$CPPCHECK_OUTCOME" != "success" ]; then echo "::error::Step 'cppcheck' failed (outcome: $CPPCHECK_OUTCOME)" failed=1 fi if [ "$FLAKE8_OUTCOME" != "success" ]; then echo "::error::Step 'flake8' failed (outcome: $FLAKE8_OUTCOME)" failed=1 fi if [ "$PHPCS_OUTCOME" != "success" ]; then echo "::error::Step 'phpcs' failed (outcome: $PHPCS_OUTCOME)" failed=1 fi if [ "$RUBOCOP_OUTCOME" != "success" ]; then echo "::error::Step 'rubocop' failed (outcome: $RUBOCOP_OUTCOME)" failed=1 fi exit $failed