Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#94]: Improve report_pr_changes_only #95

54 changes: 25 additions & 29 deletions docker/static_analysis.dockerfile
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
FROM ubuntu:22.10 as base
FROM ubuntu:23.04 as base

# Define versions as environment variables
ENV CLANG_VERSION=16
ENV CPPCHECK_VERSION=2.12.0

# Other environment variables
ENV CXX=clang++
ENV CC=clang

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y build-essential \
python3 python3-pip git clang-15 clang-tidy-15 wget libssl-dev ninja-build && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN pip3 install PyGithub

RUN ln -s \
"$(which clang++-15)" \
/usr/bin/clang++

RUN ln -s \
"$(which clang-15)" \
/usr/bin/clang

RUN ln -s \
/usr/bin/python3 \
/usr/bin/python

RUN git clone https://github.com/Kitware/CMake.git && \
cd CMake && ./bootstrap && \
make -j4 && make install

RUN wget 'https://sourceforge.net/projects/cppcheck/files/cppcheck/2.9/cppcheck-2.9.tar.gz/download' && \
tar xf download && \
cd cppcheck-2.9 && mkdir build && cd build && \
cmake -G "Ninja" .. && ninja install

# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential python3 python3-pip git clang-$CLANG_VERSION clang-tidy-$CLANG_VERSION wget libssl-dev ninja-build \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& pip3 install PyGithub --break-system-packages \
&& ln -s "$(which clang++-$CLANG_VERSION)" /usr/bin/clang++ \
&& ln -s "$(which clang-$CLANG_VERSION)" /usr/bin/clang \
&& ln -s /usr/bin/python3 /usr/bin/python

WORKDIR /opt

# Build CMake from source
RUN git clone https://github.com/Kitware/CMake.git \
&& cd CMake && ./bootstrap && make -j4 && make install

# Install cppcheck
RUN git clone https://github.com/danmar/cppcheck.git \
&& cd cppcheck && git checkout tags/$CPPCHECK_VERSION && mkdir build && cd build && cmake -G Ninja .. && ninja all && ninja install
35 changes: 26 additions & 9 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ if [ "$GITHUB_EVENT_NAME" = "pull_request_target" ] && [ -n "$INPUT_PR_REPO" ];
export GITHUB_WORKSPACE=$(pwd)
fi

preselected_files=""
if [ "$INPUT_REPORT_PR_CHANGES_ONLY" = true ]; then
echo "The 'report_pr_changes_only' option is enabled. Running SA only on modified files."
git config --global --add safe.directory /github/workspace
git fetch origin
common_ancestor=$(git merge-base origin/"$GITHUB_BASE_REF" "origin/$GITHUB_HEAD_REF")
preselected_files="$(git diff --name-only "$common_ancestor" "origin/$GITHUB_HEAD_REF" | grep -E '\.(c|cpp|h|hpp)$')"
debug_print "Preselected files: \n$preselected_files"
fi

debug_print "GITHUB_WORKSPACE = ${GITHUB_WORKSPACE} INPUT_EXCLUDE_DIR = ${INPUT_EXCLUDE_DIR} use_extra_directory = ${use_extra_directory}"

mkdir -p build
Expand All @@ -67,15 +77,18 @@ fi
cd build

if [ "$INPUT_USE_CMAKE" = true ]; then
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON "$INPUT_CMAKE_ARGS" ..
# Trim trailing newlines
INPUT_CMAKE_ARGS="${INPUT_CMAKE_ARGS%"${INPUT_CMAKE_ARGS##*[![:space:]]}"}"
debug_print "Running cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON $INPUT_CMAKE_ARGS -S $GITHUB_WORKSPACE -B $(pwd)"
eval "cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON $INPUT_CMAKE_ARGS -S $GITHUB_WORKSPACE -B $(pwd)"
fi

if [ -z "$INPUT_EXCLUDE_DIR" ]; then
files_to_check=$(python3 /get_files_to_check.py -dir="$GITHUB_WORKSPACE")
debug_print "Running: files_to_check=python3 /get_files_to_check.py -dir=\"$GITHUB_WORKSPACE\")"
files_to_check=$(python3 /get_files_to_check.py -dir="$GITHUB_WORKSPACE" -preselected="$preselected_files")
debug_print "Running: files_to_check=python3 /get_files_to_check.py -dir=\"$GITHUB_WORKSPACE\" -preselected=\"$preselected_files\")"
else
files_to_check=$(python3 /get_files_to_check.py -exclude="$GITHUB_WORKSPACE/$INPUT_EXCLUDE_DIR" -dir="$GITHUB_WORKSPACE")
debug_print "Running: files_to_check=python3 /get_files_to_check.py -exclude=\"$GITHUB_WORKSPACE/$INPUT_EXCLUDE_DIR\" -dir=\"$GITHUB_WORKSPACE\")"
files_to_check=$(python3 /get_files_to_check.py -exclude="$GITHUB_WORKSPACE/$INPUT_EXCLUDE_DIR" -dir="$GITHUB_WORKSPACE" -preselected="$preselected_files")
debug_print "Running: files_to_check=python3 /get_files_to_check.py -exclude=\"$GITHUB_WORKSPACE/$INPUT_EXCLUDE_DIR\" -dir=\"$GITHUB_WORKSPACE\" -preselected=\"$preselected_files\")"
fi

debug_print "Files to check = $files_to_check"
Expand All @@ -90,14 +103,18 @@ if [ "$INPUT_USE_CMAKE" = true ]; then
debug_print "Running cppcheck --project=compile_commands.json $INPUT_CPPCHECK_ARGS --output-file=cppcheck.txt -i$GITHUB_WORKSPACE/$INPUT_EXCLUDE_DIR ..."
eval cppcheck --project=compile_commands.json "$INPUT_CPPCHECK_ARGS" --output-file=cppcheck.txt -i"$GITHUB_WORKSPACE/$INPUT_EXCLUDE_DIR" "$GITHUB_WORKSPACE" || true
fi

# Excludes for clang-tidy are handled in python script
debug_print "Running run-clang-tidy-16 $INPUT_CLANG_TIDY_ARGS -p $(pwd) $files_to_check >>clang_tidy.txt 2>&1"
eval run-clang-tidy-16 "$INPUT_CLANG_TIDY_ARGS" -p "$(pwd)" "$files_to_check" >clang_tidy.txt 2>&1 || true

else
# Excludes for clang-tidy are handled in python script
debug_print "Running cppcheck $files_to_check $INPUT_CPPCHECK_ARGS --output-file=cppcheck.txt ..."
eval cppcheck "$files_to_check" "$INPUT_CPPCHECK_ARGS" --output-file=cppcheck.txt || true
fi

# Excludes for clang-tidy are handled in python script
debug_print "Running run-clang-tidy-15 $INPUT_CLANG_TIDY_ARGS -p $(pwd) $files_to_check >>clang_tidy.txt 2>&1"
eval run-clang-tidy-15 "$INPUT_CLANG_TIDY_ARGS" -p "$(pwd)" "$files_to_check" >clang_tidy.txt 2>&1 || true
debug_print "Running run-clang-tidy-16 $INPUT_CLANG_TIDY_ARGS $files_to_check >>clang_tidy.txt 2>&1"
eval run-clang-tidy-16 "$INPUT_CLANG_TIDY_ARGS" "$files_to_check" >clang_tidy.txt 2>&1 || true
fi

python3 /run_static_analysis.py -cc cppcheck.txt -ct clang_tidy.txt -o "$print_to_console" -fk "$use_extra_directory"
30 changes: 21 additions & 9 deletions src/get_files_to_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from pathlib import Path


def get_files_to_check(directory_in, excludes_in):
def get_files_to_check(directory_in, excludes_in, preselected_files):
"""
Given a directory path and a string of prefixes to exclude,
return a space-separated string of all files in the directory (and its subdirectories)
Expand All @@ -11,6 +11,7 @@ def get_files_to_check(directory_in, excludes_in):
Args:
directory_in (str): The path to the directory to search for files.
excludes_in (str): A space-separated string of prefixes to exclude from the search.
preselected_files (str): If present, then it's the list of files to be checked (minus excluded ones)

Returns:
str: A space-separated string of file paths that meet the search criteria.
Expand All @@ -26,22 +27,33 @@ def get_files_to_check(directory_in, excludes_in):
supported_extensions = (".h", ".hpp", ".hcc", ".c", ".cc", ".cpp", ".cxx")
all_files = []

for path in Path(directory_in).rglob("*.*"):
path_ = str(path.resolve())
if path_.endswith(supported_extensions) and not path_.startswith(
tuple(exclude_prefixes)
):
all_files.append(path_)
if len(preselected_files) == 0:
for path in Path(directory_in).rglob("*.*"):
path_ = str(path.resolve())
if path_.endswith(supported_extensions) and not path_.startswith(
tuple(exclude_prefixes)
):
all_files.append(path_)
else:
for file in preselected_files:
if not file.startswith(directory_in):
file = f"{directory_in}/{file}"
if not file.startswith(tuple(exclude_prefixes)):
all_files.append(file)

return " ".join(all_files)


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-exclude", help="Exclude prefix", required=False)
parser.add_argument("-dir", help="Current directory", required=True)
parser.add_argument(
"-preselected", help="Preselected files", default="", required=False
)
parser.add_argument("-dir", help="Source directory", required=True)

directory = parser.parse_args().dir
preselected = parser.parse_args().preselected
excludes = parser.parse_args().exclude

print(get_files_to_check(directory, excludes))
print(get_files_to_check(directory, excludes, preselected.split()))
13 changes: 11 additions & 2 deletions test/test_static_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def test_get_files_to_check(self):
f"{pwd}/utils/dummy_project/exclude_dir_2/ExcludedFile2.hpp",
]
result = get_files_to_check.get_files_to_check(
f"{pwd}/utils/dummy_project", None
f"{pwd}/utils/dummy_project", None, ""
)

self.assertEqual(to_list_and_sort(result), expected)
Expand All @@ -197,7 +197,7 @@ def test_get_files_to_check(self):
f"{pwd}/utils/dummy_project/exclude_dir_2/ExcludedFile2.hpp",
]
result = get_files_to_check.get_files_to_check(
f"{pwd}/utils/dummy_project", f"{pwd}/utils/dummy_project/exclude_dir_1"
f"{pwd}/utils/dummy_project", f"{pwd}/utils/dummy_project/exclude_dir_1", ""
)

self.assertEqual(to_list_and_sort(result), expected)
Expand All @@ -210,6 +210,15 @@ def test_get_files_to_check(self):
result = get_files_to_check.get_files_to_check(
f"{pwd}/utils/dummy_project",
f"{pwd}/utils/dummy_project/exclude_dir_1 {pwd}/utils/dummy_project/exclude_dir_2",
"",
)

# Preselected files present
expected = [f"{pwd}/utils/dummy_project/DummyFile.cpp"]
result = get_files_to_check.get_files_to_check(
f"{pwd}/utils/dummy_project",
f"{pwd}/utils/dummy_project/exclude_dir_1 {pwd}/utils/dummy_project/exclude_dir_2",
f"{pwd}/utils/dummy_project/DummyFile.cpp {pwd}/utils/dummy_project/exclude_dir_1/ExcludedFile1.hpp",
)


Expand Down