| 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 |