From ed82bd1280b6495bb3f755092569d151cf2e8c70 Mon Sep 17 00:00:00 2001 From: Floriansimmer Date: Sat, 23 Jul 2022 18:53:50 +0200 Subject: [PATCH] Github: basic check CI Run (#7152) * Github: basic check CI Run - trailing Whitespace - tab - string based QT connections - Cpplint - pylint - Black - clang-format - Codespell - clang-tidy - clazy (+QT6 Checks) - Report --- .github/problemMatcher/blackWarning.json | 15 + .github/problemMatcher/clang.json | 17 + .github/problemMatcher/codespell.json | 16 + .github/problemMatcher/cpplint.json | 16 + .../problemMatcher/grepMatcherWarning.json | 16 + .github/problemMatcher/pylintError.json | 18 + .github/problemMatcher/pylintWarning.json | 18 + .github/workflows/checks.yml | 796 ++++++++++++++++++ 8 files changed, 912 insertions(+) create mode 100644 .github/problemMatcher/blackWarning.json create mode 100644 .github/problemMatcher/clang.json create mode 100644 .github/problemMatcher/codespell.json create mode 100644 .github/problemMatcher/cpplint.json create mode 100644 .github/problemMatcher/grepMatcherWarning.json create mode 100644 .github/problemMatcher/pylintError.json create mode 100644 .github/problemMatcher/pylintWarning.json create mode 100644 .github/workflows/checks.yml diff --git a/.github/problemMatcher/blackWarning.json b/.github/problemMatcher/blackWarning.json new file mode 100644 index 0000000000..2d48de1576 --- /dev/null +++ b/.github/problemMatcher/blackWarning.json @@ -0,0 +1,15 @@ +{ + "problemMatcher": [ + { + "owner": "black-warning", + "severity": "warning", + "pattern": [ + { + "regexp": "^(would reformat (.*))$", + "file": 2, + "message": 1 + } + ] + } + ] +} diff --git a/.github/problemMatcher/clang.json b/.github/problemMatcher/clang.json new file mode 100644 index 0000000000..b8a1185427 --- /dev/null +++ b/.github/problemMatcher/clang.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "clang", + "pattern": [ + { + "regexp": "^(.+):([0-9]+):([0-9]+): (error|warning|note): (.+)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] + } + ] +} diff --git a/.github/problemMatcher/codespell.json b/.github/problemMatcher/codespell.json new file mode 100644 index 0000000000..e8f4f8381f --- /dev/null +++ b/.github/problemMatcher/codespell.json @@ -0,0 +1,16 @@ +{ + "problemMatcher": [ + { + "owner": "codespell-matcher", + "severity": "warning", + "pattern": [ + { + "regexp": "^(.+):(\\d+):\\s+(.+)$", + "file": 1, + "line": 2, + "message": 3 + } + ] + } + ] +} diff --git a/.github/problemMatcher/cpplint.json b/.github/problemMatcher/cpplint.json new file mode 100644 index 0000000000..79efedb0f1 --- /dev/null +++ b/.github/problemMatcher/cpplint.json @@ -0,0 +1,16 @@ +{ + "problemMatcher": [ + { + "owner": "cpplint", + "severity": "warning", + "pattern": [ + { + "regexp": "^(.+):([0-9]+): (.+)$", + "file": 1, + "line": 2, + "message": 3 + } + ] + } + ] +} diff --git a/.github/problemMatcher/grepMatcherWarning.json b/.github/problemMatcher/grepMatcherWarning.json new file mode 100644 index 0000000000..49671d4253 --- /dev/null +++ b/.github/problemMatcher/grepMatcherWarning.json @@ -0,0 +1,16 @@ +{ + "problemMatcher": [ + { + "owner": "grepMatcher-warning", + "severity": "warning", + "pattern": [ + { + "regexp": "^(.+):([0-9]+):(.+)$", + "file": 1, + "line": 2, + "message": 3 + } + ] + } + ] +} diff --git a/.github/problemMatcher/pylintError.json b/.github/problemMatcher/pylintError.json new file mode 100644 index 0000000000..8c5f73fbf5 --- /dev/null +++ b/.github/problemMatcher/pylintError.json @@ -0,0 +1,18 @@ +{ + "problemMatcher": [ + { + "owner": "pylint-error", + "severity": "error", + "pattern": [ + { + "regexp": "^(.+?):([0-9]+):([0-9]+): ([([E|F][0-9]+): (.*)$", + "file": 1, + "line": 2, + "column": 3, + "code": 4, + "message": 5 + } + ] + } + ] +} diff --git a/.github/problemMatcher/pylintWarning.json b/.github/problemMatcher/pylintWarning.json new file mode 100644 index 0000000000..6a453f1d43 --- /dev/null +++ b/.github/problemMatcher/pylintWarning.json @@ -0,0 +1,18 @@ +{ + "problemMatcher": [ + { + "owner": "pylint-warning", + "severity": "warning", + "pattern": [ + { + "regexp": "^(.+?):([0-9]+):([0-9]+): ([^E|F][0-9]+): (.*)$", + "file": 1, + "line": 2, + "column": 3, + "code": 4, + "message": 5 + } + ] + } + ] +} diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 0000000000..133eafb55f --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,796 @@ +name: CI +on: + pull_request: + +permissions: + pull-requests: write + + +env: + logdir: logs/ + fixesdir: fixes/ + + checkWhitespace: true + whitespaceFailSilent: true + + checkTabs: true + tabsFailSilent: true + + checkQtconnections: true + qtconnectionSyntaxFailSilent: true + + checkCpplint: true + cpplintFilters: -build/header_guard,-readability/braces,-whitespace + cpplintLineLength: 120 + cpplintFailSilent: true + + checkPylint: true + pylintDisable: E0401,C0301,C0103,R0801,W0125 + # for the Message codes see: https://pylint.pycqa.org/en/latest/user_guide/messages/index.html + # E0401: Unable to import '*' (import-error) + # C0103: Function name "*" doesn't conform to snake_case naming style (invalid-name) + # C0301: Line too long (118/100) (line-too-long) + # C0115: Missing class docstring (missing-class-docstring) + # C0116: Missing function or method docstring (missing-function-docstring) + # C0411: standard import "import *" should be placed before "import *" (wrong-import-order) + # R0801: Similar lines in 2 files --> not useful for us because not all files will be checked + # W0125: Using a conditional statement with a constant value (using-constant-test) --> often used for testing/debugging purposes in the Path module + pylintFailSilent: false + + checkBlack: true + blackFailSilent: true + + checkClangFormat: true + clangStyle: file #for .clang-format file + clangFormatFailSilent: true + + checkSpelling: true + listIgnoredMisspelling: .github/codespellignore + skip: ./.git,*.po,*.ts,*.svg,./ChangeLog.txt,./src/3rdParty,./src/Mod/Assembly/App/opendcm,./src/CXX,./src/zipios++,./src/Base/swig*,./src/Mod/Robot/App/kdl_cp,./src/Mod/Import/App/SCL,./src/WindowsInstaller,./src/Doc/FreeCAD.uml,./build/ + codespellFailSilent: false + + checkClangTidy: true + clangTidyChecks: boost-*,bugprone-*,performance-*,readability-*,portability-*,modernize-*,clang-analyzer-*,cppcoreguidelines-*,concurrency-* + clangTidyFailSilent: false + + checkClazy: true + clazyChecks: level1 # https://invent.kde.org/sdk/clazy#list-of-checks + clazyFailSilent: false + + checkClazyQT6: true + clazyQT6Checks: qt6-deprecated-api-fixes,qt6-header-fixes,qt6-qhash-signature,qt6-fwd-fixes,missing-qobject-macro # for QT6 Porting https://invent.kde.org/sdk/clazy#list-of-checks + QT6Branch: master # branch to check for QT6 Porting + clazyQT6FailSilent: false + + reportStepSummary: true + reportPRComment: false + +jobs: + checks: + name: Checks + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 2 + + - name: Install dependencies + id: install-dependencies + run: | + sudo apt-get update -qq + + # apt-get install all the dependencies + # TODO: script to install all the dependencies (tools/build/ubuntu.sh) not working + # chmod +x tools/build/ubuntu.sh + # sudo ./tools/build/ubuntu.sh + + sudo apt-get install -y --no-install-recommends \ + python3-dev \ + python3-matplotlib \ + python3-pivy \ + python3-ply \ + python3-pyside2.qtcore \ + python3-pyside2.qtgui \ + python3-pyside2.qtsvg \ + python3-pyside2.qtwidgets \ + python3-pyside2.qtnetwork \ + python3-markdown \ + python3-git \ + libboost-date-time-dev \ + libboost-dev \ + libboost-filesystem-dev \ + libboost-graph-dev \ + libboost-iostreams-dev \ + libboost-program-options-dev \ + libboost-python-dev \ + libboost-regex-dev \ + libboost-serialization-dev \ + libboost-thread-dev \ + libxerces-c-dev \ + libocct-data-exchange-dev \ + libocct-ocaf-dev \ + libocct-visualization-dev \ + occt-draw \ + libvtk7-dev \ + swig \ + libcoin-dev \ + libeigen3-dev \ + libgts-bin \ + libgts-dev \ + libkdtree++-dev \ + libmedc-dev \ + libopencv-dev \ + libproj-dev \ + libpyside2-dev \ + pyside2-tools \ + pyqt5-dev-tools \ + libqt5opengl5-dev \ + libqt5svg5-dev \ + libqt5webkit5-dev \ + libqt5x11extras5-dev \ + libqt5xmlpatterns5-dev \ + qtbase5-dev \ + qttools5-dev \ + libx11-dev \ + libzipios++-dev \ + doxygen \ + graphviz \ + libnglib-dev \ + netgen \ + netgen-headers \ + libmetis-dev \ + libspnav-dev \ + libshiboken2-dev \ + shiboken2 \ + imagemagick + + - name: make Log and Fixes directory + run: | + mkdir -p ${{ env.logdir }} + mkdir -p ${{ env.fixesdir }} + + - name: Job summary Header + id: header + run: | + echo '# Check Results: :rocket:' >> ${{ env.logdir }}checkReport.md + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v12.2 + + - name: List all changed Files + id: list-changed-files + run: | + echo "
:clipboard: Changed Files:" >> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + echo '``` shell' >> ${{ env.logdir }}checkReport.md + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + echo "$file" >> ${{ env.logdir }}checkReport.md + done + echo '```' >> ${{ env.logdir }}checkReport.md + echo '
' >> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + + - name: Harmonize Line endings + id: harmonize-line-endings + shell: bash + run: | + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + if [ -f "$file" ]; then + echo "Harmonizing line endings in $file" + sed -i 's/\r//g' $file + fi + done + + - name: filter files by extensions + id: files-by-extensions + shell: bash + run: | + changed_files=$( echo ${{ steps.changed-files.outputs.all_changed_files }} ) + + py=$(grep -oE '\S*?\.(py|py3)' <<< $changed_files | tr '\n' ' ') || true # grep returns non-zero exit code if no match is found + cpp=$(grep -oE '\S*?\.(c|c++|cc|cpp|cu|cuh|cxx|h|h++|hh|hpp|hxx)' <<< $changed_files | tr '\n' ' ') || true + echo "::set-output name=python_files::$py" + echo "::set-output name=cpp_files::$cpp" + + - name: Tests Header + if: always() + id: tests-header + run: | + echo '### Tests:' >> ${{ env.logdir }}checkReport.md + + - name: Trailing whitespaces + shell: bash + if: env.checkWhitespace == 'true' && always() + run: | + whitespaceErrors="0" + exclude="*[.md]" + + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + # check for whitespaces + grep -rnIHE --exclude="$exclude" " $" $file | sed 's/$/<-- trailing whitespace/' >> ${{ env.logdir }}whitespace.log || true + done + + if [ -f ${{ env.logdir }}whitespace.log ]; then + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/grepMatcherWarning.json" + cat ${{ env.logdir }}whitespace.log + echo "::remove-matcher owner=grepMatcher-warning::" + + whitespaceErrors=$(wc -l < ${{ env.logdir }}whitespace.log) + fi + + echo "Found $whitespaceErrors whitespace errors" + + echo "whitespaceErrors=$whitespaceErrors" >> $GITHUB_ENV + + if [ "$whitespaceErrors" -gt 0 ]; then + # Write the report + echo "
:information_source: Found $whitespaceErrors trailing whitespace" >> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + head -n 500 ${{ env.logdir }}whitespace.log >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + echo "
">> ${{ env.logdir }}checkReport.md + else + echo ":heavy_check_mark: No trailing whitespace found " >> ${{ env.logdir }}checkReport.md + fi + echo "" >> ${{ env.logdir }}checkReport.md + + if [ "$whitespaceErrors" -gt 0 ] && [ ${{ env.whitespaceFailSilent }} != true ]; then + exit 1 + else + exit 0 + fi + + - name: Tabs + shell: bash + if: env.checkTabs == 'true' && always() + run: | + tabErrors="0" + exclude="*[.md]" + + # check for tabs + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + grep -rnIHE --exclude="$exclude" $'\t' $file | sed 's/$/ <-- contains tab/' >> ${{ env.logdir }}tab.log || true + done + + # Parse the log + if [ -f ${{ env.logdir }}tab.log ]; then + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/grepMatcherWarning.json" + cat ${{ env.logdir }}tab.log + echo "::remove-matcher owner=grepMatcher-warning::" + + tabErrors=$(wc -l < ${{ env.logdir }}tab.log) + fi + + echo "Found $tabErrors tab errors" + + echo "tabErrors=$tabErrors" >> $GITHUB_ENV + + if [ "$tabErrors" -gt 0 ]; then + echo "
:information_source: Found $tabErrors tabs, better to use spaces" >> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + head -n 500 ${{ env.logdir }}tab.log >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + echo "
">> ${{ env.logdir }}checkReport.md + else + echo ":heavy_check_mark: No tabs found" >> ${{ env.logdir }}checkReport.md + fi + echo "" >> ${{ env.logdir }}checkReport.md + + if [ "$tabErrors" -gt 0 ] && [ ${{ env.tabsFailSilent }} != true ]; then + exit 1 + else + exit 0 + fi + + - name: QT - old string-based connections (https://wiki.qt.io/New_Signal_Slot_Syntax) + shell: bash + if: env.checkQtconnections == 'true' && steps.files-by-extensions.outputs.cpp_files != '' && always() + run: | + qtconnectionSyntax="0" + exclude="*[.md,.log,.ts,.git]" + + # check all files for QT string-based connections + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + grep -rnIHE --exclude="$exclude" $' SIGNAL| SLOT' $file | sed 's,$, <--Consider using Functor-Based Connections,' >> ${{ env.logdir }}stringSyntax.log || true + done + + # Parse the log file + if [ -f ${{ env.logdir }}stringSyntax.log ]; then + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/grepMatcherWarning.json" + cat ${{ env.logdir }}stringSyntax.log + echo "::remove-matcher owner=grepMatcher-warning::" + + qtconnectionSyntax=$(wc -l < ${{ env.logdir }}stringSyntax.log) + fi + + # Pass Variables to GitHub Env + echo "qtconnectionSyntax=$qtconnectionSyntax" >> $GITHUB_ENV + + echo "Found $qtconnectionSyntax QT string-based connections" + + # Step-Report + if [ "$qtconnectionSyntax" -gt 0 ]; then + echo "
:information_source: Found $qtconnectionSyntax QT string-based connections :arrow_right: consider using QT functor-Based Connections" >> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + # documentation link + echo "For more information see: https://wiki.qt.io/New_Signal_Slot_Syntax or https://github.com/FreeCAD/FreeCAD/issues/6166" >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + head -n 500 ${{ env.logdir }}stringSyntax.log >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + echo "
">> ${{ env.logdir }}checkReport.md + else + echo ":heavy_check_mark: No string-based connections found " >> ${{ env.logdir }}checkReport.md + fi + echo "" >> ${{ env.logdir }}checkReport.md + + if [ "$qtconnectionSyntax" -gt 0 ] && [ ${{ env.qtconnectionSyntaxFailSilent }} != true ]; then + exit 1 + else + exit 0 + fi + + - name: Cpplint + shell: bash + if: env.checkCpplint == 'true' && steps.files-by-extensions.outputs.cpp_files != '' && always() + run: | + cpplintErrors="0" + #filters="--filter=-build/header_guard,-readability/braces,-whitespace" + #linelength="--linelength=120" + + pip install cpplint + + # run cpplint + for file in ${{ steps.files-by-extensions.outputs.cpp_files }}; do + cpplint --filter=${{ env.cpplintFilters }} --linelength=${{ env.cpplintLineLength }} $file &>> ${{ env.logdir }}cpplint.log || true + done + + # if cpplint has run successfully parse the output + if [ -f ${{ env.logdir }}cpplint.log ]; then + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/cpplint.json" + cat ${{ env.logdir }}cpplint.log + echo "::remove-matcher owner=cpplint::" + + cpplintErrors=$(grep -rnIHE "\[[0-9]\]$" ${{ env.logdir }}cpplint.log | wc -l ) || true + fi + + # Pass Variables to GitHub Env + echo "cpplintErrors=$cpplintErrors" >> $GITHUB_ENV + + echo "Found $cpplintErrors cpplint errors" + + # Step-Report + if [ "$cpplintErrors" -gt 0 ]; then + echo "
:warning: CppLint found $cpplintErrors errors / warnings" >> ${{ env.logdir }}checkReport.md + else + echo "
:heavy_check_mark: No cpplint errors found " >> ${{ env.logdir }}checkReport.md + fi + + echo "" >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + head -n 500 ${{ env.logdir }}cpplint.log >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + echo "
">> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + + if [ "$cpplintErrors" -gt 0 ] && [ ${{ env.cpplintFailSilent }} != true ]; then + exit 1 + else + exit 0 + fi + + - name: Pylint + shell: bash + if: env.checkPylint == 'true' && steps.files-by-extensions.outputs.python_files != '' && always() + run: | + pylintErrors="0" + + pip install pylint + + pylintErrors="0" + pylintWarnings="0" + pylintRefactorings="0" + pylintConventions="0" + + # Run pylint on all python files + pylint --exit-zero --reports y --disable=${{ env.pylintDisable }} ${{ steps.files-by-extensions.outputs.python_files }} > ${{ env.logdir }}pylint.log + + # if pylint has run successfully, then parse the output + if [ -f ${{ env.logdir }}pylint.log ]; then + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/pylintError.json" + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/pylintWarning.json" + cat ${{ env.logdir }}pylint.log + echo "::remove-matcher owner=pylint-error::" + echo "::remove-matcher owner=pylint-warning::" + + pylintErrors=$( grep -oP '(?<=error \|)\d+' ${{ env.logdir }}pylint.log) || true # grep returns 0 if no match is found + pylintWarnings=$( grep -oP '(?<=warning \|)\d+' ${{ env.logdir }}pylint.log) || true + pylintRefactorings=$( grep -oP '(?<=refactor \|)\d+' ${{ env.logdir }}pylint.log) || true + pylintConventions=$( grep -oP '(?<=convention \|)\d+' ${{ env.logdir }}pylint.log) || true + fi + + # Pass Variables to GitHub Env + echo "pylintErrors=$pylintErrors" >> $GITHUB_ENV + echo "pylintWarnings=$pylintWarnings" >> $GITHUB_ENV + echo "pylintRefactorings=$pylintRefactorings" >> $GITHUB_ENV + echo "pylintConventions=$pylintConventions" >> $GITHUB_ENV + + echo "Found $pylintErrors errors, $pylintWarnings warnings, $pylintRefactorings refactorings, $pylintConventions conventions" + + #Step-summary + if [ "$pylintErrors" -gt 0 ]; then + echo "
:fire: Pylint found :fire: $pylintErrors errors, :warning: $pylintWarnings warnings, :construction: $pylintRefactorings refactorings and :pencil2: $pylintConventions conventions" >> ${{ env.logdir }}checkReport.md + elif [ "$pylintWarnings" -gt 0 ]; then + echo "
:warning: Pylint found :warning: $pylintWarnings warnings, :construction: $pylintRefactorings refactorings and :pencil2: $pylintConventions conventions" >> ${{ env.logdir }}checkReport.md + elif [ "$pylintRefactorings" -gt 0 ]; then + echo "
:construction: Pylint found :construction: $pylintRefactorings refactorings and :pencil2: $pylintConventions conventions" >> ${{ env.logdir }}checkReport.md + elif [ "$pylintConventions" -gt 0 ]; then + echo "
:pencil2: Pylint found :pencil2: $pylintConventions conventions" >> ${{ env.logdir }}checkReport.md + else + echo "
:heavy_check_mark: No pylint errors found " >> ${{ env.logdir }}checkReport.md + fi + + echo "" >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + head -n 500 ${{ env.logdir }}pylint.log >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + echo "
">> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + + # Error the step if pylint has found errors + if [ "$pylintErrors" -gt 0 ] && [ ${{ env.pylintFailSilent }} != true ]; then + exit 1 + else + exit 0 + fi + + - name: Black (Python) + shell: bash + if: env.checkBlack == 'true' && steps.files-by-extensions.outputs.python_files != '' && always() + run: | + + pip install black + + blackReformats="0" + blackFails="0" + + black --check ${{ steps.files-by-extensions.outputs.python_files }} &> ${{ env.logdir }}black.log || true + + # if black has run successfully, then parse the output + if [ -f ${{ env.logdir }}black.log ]; then + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/blackWarning.json" + cat ${{ env.logdir }}black.log + echo "::remove-matcher owner=black-warning::" + + blackReformats=$( grep -oP '\d+(?= fil.+ would be reformatted)' ${{ env.logdir }}black.log) || true # grep returns 0 if no match is found + blackFails=$( grep -oP '\d+(?= fil.+ would fail to reformat)' ${{ env.logdir }}black.log) || true + fi + + # Pass Variables to GitHub Env + echo "blackReformats=$blackReformats" >> $GITHUB_ENV + echo "blackFails=$blackFails" >> $GITHUB_ENV + + echo "Found $blackReformats files would be reformatted and $blackFails files would fail to reformat" + + #Step-summary + if [ "$blackReformats" -gt 0 ] || [ "$blackFails" -gt 0 ] ; then + echo "
:pencil2: Black would reformat $blackReformats files" >> ${{ env.logdir }}checkReport.md + else + echo "
:heavy_check_mark: Black would reformat no file" >> ${{ env.logdir }}checkReport.md + fi + + echo "" >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + head -n 500 ${{ env.logdir }}black.log >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + echo "
">> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + + if [ "$blackReformats" -gt 0 ] && [ ${{ env.blackFailSilent }} != true ]; then + exit 1 + else + exit 0 + fi + + - name: Clang-format + shell: bash + if: env.checkClangFormat == 'true' && steps.files-by-extensions.outputs.cpp_files != '' && always() + run: | + clangFormatErrors="0" + + # run clang-format on all cpp files + clang-format --dry-run --ferror-limit=1 --verbose --style=${{ env.clangStyle }} ${{ steps.files-by-extensions.outputs.cpp_files }} &>> ${{ env.logdir }}clang-format.log || true + + # if cpplint has run successfully parse the output + if [ -f ${{ env.logdir }}clang-format.log ]; then + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/clang.json" + cat ${{ env.logdir }}clang-format.log + echo "::remove-matcher owner=clang::" + + clangFormatErrors=$(grep -rnIHE "\[-Wclang-format-violations]$" ${{ env.logdir }}clang-format.log | wc -l ) || true + fi + + # Pass Variables to GitHub Env + echo "clangFormatErrors=$clangFormatErrors" >> $GITHUB_ENV + + echo "Found $clangFormatErrors clang-format errors" + + # Report + + if [ "$clangFormatErrors" -gt 0 ]; then + echo "
:pencil2: Clang-format would reformat $clangFormatErrors files" >> ${{ env.logdir }}checkReport.md + else + echo "
:heavy_check_mark: Clang-format would reformat no file " >> ${{ env.logdir }}checkReport.md + fi + + echo "" >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + head -n 500 ${{ env.logdir }}clang-format.log >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + echo "
">> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + + if [ "$clangFormatErrors" -gt 0 ] && [ ${{ env.clangFormatFailSilent }} != true ]; then + exit 1 + else + exit 0 + fi + + - name: Codespell + shell: bash + if: env.checkSpelling == 'true' && always() + run: | + pip install codespell + + wget https://raw.githubusercontent.com/codespell-project/codespell/master/codespell_lib/data/dictionary.txt + #wget https://raw.githubusercontent.com/codespell-project/codespell/master/codespell_lib/data/dictionary_rare.txt + + #misspellings=$( codespell --quiet-level 3 --summary --count --ignore-words ${{ env.listIgnoredMisspelling }} --skip ${{ env.skip }} -D dictionary.txt -D dictionary_rare.txt ${{ steps.changed-files.outputs.all_changed_files }} 2>&1 > ${{ env.logdir }}codespell.log ) || true + misspellings=$( codespell --quiet-level 3 --summary --count --ignore-words ${{ env.listIgnoredMisspelling }} --skip ${{ env.skip }} -D dictionary.txt ${{ steps.changed-files.outputs.all_changed_files }} 2>&1 > ${{ env.logdir }}codespell.log ) || true + + # if codespell has run successfully, then parse the output + if [ -f ${{ env.logdir }}codespell.log ]; then + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/codespell.json" + cat ${{ env.logdir }}codespell.log + echo "::remove-matcher owner=codespell::" + fi + + # Pass Variables to GitHub Env + echo "CodespellMisspellings=$misspellings" >> $GITHUB_ENV + + echo "Found $misspellings misspellings" + + #Step-summary + if [ "$misspellings" -gt 0 ]; then + echo "
:pencil2: Codespell found $misspellings misspellings" >> ${{ env.logdir }}checkReport.md + else + echo "
:heavy_check_mark: Codespell found no misspellings" >> ${{ env.logdir }}checkReport.md + fi + + echo "" >> ${{ env.logdir }}checkReport.md + # documentation link + echo "To ignore false positives, append the word to the [.github/codespellignore](https://github.com/FreeCAD/FreeCAD/blob/c38e88c61b8926f725215da88940025c94be1daa/.github/codespellignore) file (lowercase)" >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + head -n 500 ${{ env.logdir }}codespell.log >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + echo "
">> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + + # Error the step if pylint has found errors + if [ "$misspellings" -gt 0 ] && [ ${{ env.codespellFailSilent }} != true ]; then + exit 1 + else + exit 0 + fi + + - name: CMake Configure + if: env.checkClangTidy == 'true' && steps.files-by-extensions.outputs.cpp_files != '' && always() + run: | + cmake -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -B build/ > ${{ env.logdir }}Cmake.log + cat ${{ env.logdir }}Cmake.log + + - name: Clang-tidy + shell: bash + if: env.checkClangTidy == 'true' && steps.files-by-extensions.outputs.cpp_files != '' && always() + run: | + clangTidyErrors="0" + clangTidyWarnings="0" + clangTidyNotes="0" + + sudo apt-get install clang-tidy + + clang-tidy --quiet --format-style=${{ env.clangStyle }} --export-fixes=${{ env.fixesdir }}clang-tidy.yaml -checks=${{ env.clangTidyChecks }} -p build/ ${{ steps.files-by-extensions.outputs.cpp_files }} &>> ${{ env.logdir }}clang-tidy.log || true + + # if clang-tidy has run successfully parse the output + if [ -f ${{ env.logdir }}clang-tidy.log ]; then + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/clang.json" + cat ${{ env.logdir }}clang-tidy.log + echo "::remove-matcher owner=clang::" + + clangTidyErrors=$(grep -rnIHE "^(.+):([0-9]+):([0-9]+): error: .+$" ${{ env.logdir }}clang-tidy.log | wc -l ) || true + clangTidyWarnings=$(grep -rnIHE "^(.+):([0-9]+):([0-9]+): warning: .+$" ${{ env.logdir }}clang-tidy.log | wc -l ) || true + clangTidyNotes=$(grep -rnIHE "^(.+):([0-9]+):([0-9]+): note: .+$" ${{ env.logdir }}clang-tidy.log | wc -l ) || true + fi + + # Pass Variables to GitHub Env + echo "clangTidyErrors=$clangTidyErrors" >> $GITHUB_ENV + echo "clangTidyWarnings=$clangTidyWarnings" >> $GITHUB_ENV + echo "clangTidyNotes=$clangTidyNotes" >> $GITHUB_ENV + + echo "Found $clangTidyErrors errors, $clangTidyWarnings warnings, $clangTidyNotes notes" + + # Step-Report + if [ "$clangTidyErrors" -gt 0 ]; then + echo "
:fire: Clang-Tidy found :fire: $clangTidyErrors errors, :warning: $clangTidyWarnings warnings and :pencil2: $clangTidyNotes notes" >> ${{ env.logdir }}checkReport.md + elif [ "$clangTidyWarnings" -gt 0 ]; then + echo "
:warning: Clang-Tidy found :warning: $clangTidyWarnings warnings and :pencil2: $clangTidyNotes notes" >> ${{ env.logdir }}checkReport.md + elif [ "$clangTidyNotes" -gt 0 ]; then + echo "
:pencil2: Clang-Tidy found :pencil2: $clangTidyNotes notes" >> ${{ env.logdir }}checkReport.md + else + echo "
:heavy_check_mark: Clang-Tidy found no errors, warnings or notes" >> ${{ env.logdir }}checkReport.md + fi + + echo "" >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + head -n 500 ${{ env.logdir }}clang-tidy.log >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + echo "
">> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + + # Error the step if pylint has found errors + if [ "$clangTidyErrors" -gt 0 ] && [ ${{ env.clangTidyFailSilent }} != true ]; then + exit 1 + else + exit 0 + fi + + - name: Clazy + shell: bash + if: env.checkClazy == 'true' && steps.files-by-extensions.outputs.cpp_files != '' && always() + run: | + clazyErrors="0" + clazyWarnings="0" + clazyNotes="0" + + sudo apt-get install clazy + + clazy-standalone --export-fixes=${{ env.fixesdir }}clazy.yaml -checks=${{ env.clazyChecks }} -p build/ ${{ steps.files-by-extensions.outputs.cpp_files }} &>> ${{ env.logdir }}clazy.log || true + + # if Clazy has run successfully parse the output + if [ -f ${{ env.logdir }}clazy.log ]; then + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/clang.json" + cat ${{ env.logdir }}clazy.log + echo "::remove-matcher owner=clang::" + + clazyErrors=$(grep -rnIHE "^(.+):([0-9]+):([0-9]+): error: .+$" ${{ env.logdir }}clazy.log | wc -l ) || true + clazyWarnings=$(grep -rnIHE "^(.+):([0-9]+):([0-9]+): warning: .+$" ${{ env.logdir }}clazy.log | wc -l ) || true + clazyNotes=$(grep -rnIHE "^(.+):([0-9]+):([0-9]+): note: .+$" ${{ env.logdir }}clazy.log | wc -l ) || true + fi + + # Pass Variables to GitHub Env + echo "clazyErrors=$clazyErrors" >> $GITHUB_ENV + echo "clazyWarnings=$clazyWarnings" >> $GITHUB_ENV + echo "clazyNotes=$clazyNotes" >> $GITHUB_ENV + + echo "Found $clazyErrors errors, $clazyWarnings warnings, $clazyNotes notes" + + # Step-Report + if [ "$clazyErrors" -gt 0 ]; then + echo "
:fire: Clazy found :fire: $clazyErrors errors, :warning: $clazyWarnings warnings and :pencil2: $clazyNotes notes" >> ${{ env.logdir }}checkReport.md + elif [ "$clazyWarnings" -gt 0 ]; then + echo "
:warning: Clazy found :warning: $clazyWarnings warnings and :pencil2: $clazyNotes notes" >> ${{ env.logdir }}checkReport.md + elif [ "$clazyNotes" -gt 0 ]; then + echo "
:pencil2: Clazy found :pencil2: $clazyNotes notes" >> ${{ env.logdir }}checkReport.md + else + echo "
:heavy_check_mark: Clazy found no errors, warnings or notes" >> ${{ env.logdir }}checkReport.md + fi + + echo "" >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + head -n 500 ${{ env.logdir }}clazy.log >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + echo "
">> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + + # Error the step if pylint has found errors + if [ "$clazyErrors" -gt 0 ] && [ ${{ env.clazyFailSilent }} != true ]; then + exit 1 + else + exit 0 + fi + + - name: Clazy-QT6 + shell: bash + if: env.checkClazyQT6 == 'true' && steps.files-by-extensions.outputs.cpp_files != '' && github.base_ref == env.QT6Branch && always() + run: | + clazyQT6Errors="0" + clazyQT6Warnings="0" + clazyQT6Notes="0" + + sudo apt-get install clazy + + # clazy checks for qt6 porting + clazy-standalone --export-fixes=${{ env.fixesdir }}clazyQT6.yaml -checks=${{ env.clazyQT6Checks }} -p build/ ${{ steps.files-by-extensions.outputs.cpp_files }} &>> ${{ env.logdir }}clazyQT6.log || true + + + # if Clazy has run successfully parse the output + if [ -f ${{ env.logdir }}clazyQT6.log ]; then + echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/clang.json" + cat ${{ env.logdir }}clazyQT6.log + echo "::remove-matcher owner=clang::" + + clazyQT6Errors=$(grep -rnIHE "^(.+):([0-9]+):([0-9]+): error: .+$" ${{ env.logdir }}clazyQT6.log | wc -l ) || true + clazyQT6Warnings=$(grep -rnIHE "^(.+):([0-9]+):([0-9]+): warning: .+$" ${{ env.logdir }}clazyQT6.log | wc -l ) || true + clazyQT6Notes=$(grep -rnIHE "^(.+):([0-9]+):([0-9]+): note: .+$" ${{ env.logdir }}clazyQT6.log | wc -l ) || true + fi + + # Pass Variables to GitHub Env + echo "clazyQT6Errors=$clazyQT6Errors" >> $GITHUB_ENV + echo "clazyQT6Warnings=$clazyQT6Warnings" >> $GITHUB_ENV + echo "clazyQT6Notes=$clazyQT6Notes" >> $GITHUB_ENV + + echo "Found $clazyQT6Errors errors, $clazyQT6Warnings warnings, $clazyQT6Notes notes" + + # Step-Report + if [ "$clazyQT6Errors" -gt 0 ]; then + echo "
:fire: Clazy found :fire: $clazyQT6Errors errors, :warning: $clazyQT6Warnings warnings and :pencil2: $clazyQT6Notes notes for porting to QT6" >> ${{ env.logdir }}checkReport.md + elif [ "$clazyQT6Warnings" -gt 0 ]; then + echo "
:warning: Clazy found :warning: $clazyQT6Warnings warnings and :pencil2: $clazyQT6Notes notes for porting to QT6" >> ${{ env.logdir }}checkReport.md + elif [ "$clazyNotes" -gt 0 ]; then + echo "
:pencil2: Clazy found :pencil2: $clazyQT6Notes notes for porting to QT6" >> ${{ env.logdir }}checkReport.md + else + echo "
:heavy_check_mark: Clazy found no errors, warnings or notes for porting to QT6" >> ${{ env.logdir }}checkReport.md + fi + + echo "" >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + head -n 500 ${{ env.logdir }}clazyQT6.log >> ${{ env.logdir }}checkReport.md + echo '```' >> ${{ env.logdir }}checkReport.md + echo "
">> ${{ env.logdir }}checkReport.md + echo "" >> ${{ env.logdir }}checkReport.md + + # Error the step if pylint has found errors + if [ "$clazyQT6Errors" -gt 0 ] && [ ${{ env.clazyQT6FailSilent }} != true ]; then + exit 1 + else + exit 0 + fi + + - name: Zip Logs + if: always() + run: | + zip -r ${{ env.logdir }}logs.zip ${{ env.logdir }} + + - name: Upload Logs + if: always() + uses: actions/upload-artifact@v3 + with: + name: Logs + path: ${{ env.logdir }}logs.zip + + - name: Zip Fixes + if: always() + run: | + zip -r ${{ env.fixesdir }}fixes.zip ${{ env.fixesdir }} + + - name: Upload Fixes + if: always() + uses: actions/upload-artifact@v3 + with: + name: Fixes + path: ${{ env.fixesdir }}fixes.zip + + - name: Post Report to GITHUB_STEP_SUMMARY + if: env.reportStepSummary == 'true' && always() + id: post-report-to-github-step-summary + run: | + cat ${{ env.logdir }}checkReport.md > $GITHUB_STEP_SUMMARY + + - name: Create Pull request comment + if: env.reportPRComment == 'true' && always() + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: Check results + path: ${{ env.logdir }}checkReport.md