blob: ae41f862a1dfb2b2443566f6c2e34a3a21e828bf [file] [log] [blame]
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