name: CI on: push: branches: ['master', 'development/toponaming', 'releases/FreeCAD-0-20'] pull_request: workflow_dispatch: permissions: pull-requests: write contents: write env: logdir: logs/ fixesdir: fixes/ # Build and Test buildFailSilent: false test: true # run Unit tests testFailSilent: false # don't fail CI if Unittests fail testInstall: true # run CMake install and Unit tests on install installFailSilent: false # don't fail if CMake install fails testInstallFailSilent: false # don't fail CI if Unittests fail # Linting checkWhitespace: true whitespaceFailSilent: true checkTabs: true tabsFailSilent: true checkQtconnections: true qtconnectionSyntaxFailSilent: true checkCpplint: true # This option allows you to specify a comma-separated list of rule-filters to apply. For example, the rule names look like whitespace/indent. In that case, the category is whitespace. # To turn off, specify - as prefix like -whitespace or -whitespace/indent. On the other hand, to turn on, specify + like +whitespace or +whitespace/indent. # The recommended filters are taken from https://github.com/sider/runners/blob/HEAD/images/cpplint/sider_recommended_CPPLINT.cfg cpplintFilters: -build/c++11,-build/header_guard,-build/include,-build/include_alpha,-build/include_order,-build/include_subdir,-build/include_what_you_use,-build/namespaces,-legal/copyright,-readability/braces,-readability-braces-around-statements,-readability/casting,-readability/namespace,-readability/todo,-runtime/indentation_namespace,-runtime/int,-runtime/references,-whitespace/blank_line,-whitespace/braces,-whitespace/comma,-whitespace/comments,-whitespace/end_of_line,-whitespace/indent,-whitespace/line_length,-whitespace/newline,-whitespace/operators,-whitespace/parens,-whitespace/semicolon,-whitespace/tab,-whitespace/todo cpplintLineLength: 120 cpplintFailSilent: true checkPylint: true # additional disables to the .pylintrc file # for the Message codes see: https://pylint.pycqa.org/en/latest/user_guide/messages/index.html pylintDisable: disable=C0302 # too-many-lines checkBlack: true blackFailSilent: true checkClangFormat: false 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 # Static Analysis checkClangTidy: true # for the Message codes see: https://clang-tidy.llvm.org/checks/index.html clangTidyChecks: # empty to use the .clang-tidy file clangTidyFailSilent: true # if false, the CI will fail if clang-tidy finds errors. Warnings or notes will not fail the CI checkClazy: true # for the Message codes see: https://invent.kde.org/sdk/clazy#list-of-checks clazyChecks: level1 clazyFailSilent: true # if false, the CI will fail if clazy finds errors. Warnings or notes will not fail the CI 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: true # if false, the CI will fail if clazy finds errors. Warnings or notes will not fail the CI # Reporting reportStepSummary: true reportPRComment: false # disabled because of permission hell (and the comments are to big for Github) reportPushComment: false # disabled because of permission hell (and the comments are to big for Github) jobs: checks: name: Checks runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 with: fetch-depth: 2 - name: Install FreeCAD 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 \ qtwebengine5-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: Get Date if: always() id: get-date run: | echo "::set-output name=date::$(/bin/date -u "+%Y%m%d%H%M")" shell: bash - name: Install ccache if: always() run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends ccache - name: make directories if: always() run: | mkdir -p ${{ env.logdir }} mkdir -p ${{ env.fixesdir }} mkdir -p ~/.ccache - name: Restore Compiler Cache if: always() uses: pat-s/always-upload-cache@v3 with: path: | ~/.ccache key: CI-cache-${{ steps.get-date.outputs.date }}-${{ github.run_number }} restore-keys: | CI-cache-${{ steps.get-date.outputs.date }}- CI-cache- - name: Configure Cache if: always() run: | ccache -z -M 1G ccache --show-config ccache -s - name: Job summary Header if: always() id: header run: | echo '# Check Results: :rocket:' >> ${{ env.logdir }}checkReport.md - name: Get changed files if: always() id: changed-files uses: tj-actions/changed-files@v24 - name: Report 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 if: false #disabled because it causes failure when applied on .FCStd files id: harmonize-line-endings shell: bash run: | # harmonize line endings in all changed files (Windows -> Unix) 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 if: always() 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" echo "Python Files: $py" echo "C++ Files: $cpp" - name: CMake Configure id: configure if: always() run: | set +e cmake \ -D CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE \ -B build/ > ${{ env.logdir }}Cmake.log 2> ${{ env.logdir }}CmakeErrors.log exitCode=$? # Build Header in Report echo '### Build:' >> ${{ env.logdir }}checkReport.md # Write the configure report if [ $exitCode -eq 0 ] ; then echo "
:heavy_check_mark: CMake configure succeeded" >> ${{ env.logdir }}checkReport.md else echo "
:fire: CMake configure failed" >> ${{ env.logdir }}checkReport.md fi echo "" >> ${{ env.logdir }}checkReport.md echo "Configure Error Log (stderr output):" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md cat ${{ env.logdir }}CmakeErrors.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "Configure Log (stdout output):" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md tail -n 60 ${{ env.logdir }}Cmake.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "
">> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md cat ${{ env.logdir }}CmakeErrors.log echo "::group::Configure Log" cat ${{ env.logdir }}Cmake.log echo "::endgroup::" exit $exitCode - name: Print ccache statistics before Build if: always() run: ccache -s - name: CMake Build id: build if: always() run: | set +e cmake --build build/ > ${{ env.logdir }}Build.log 2> ${{ env.logdir }}BuildErrors.log exitCode=$? # Write the build report if [ $exitCode -eq 0 ] ; then echo "
:heavy_check_mark: CMake build succeeded" >> ${{ env.logdir }}checkReport.md else echo "
:fire: CMake build failed" >> ${{ env.logdir }}checkReport.md fi echo "" >> ${{ env.logdir }}checkReport.md echo "Build Error Log (stderr output):" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md cat ${{ env.logdir }}BuildErrors.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "Build Log (stdout output trimmed to the last 100 Lines):" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md tail -n 50 ${{ env.logdir }}Build.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "
">> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md # Print the Log to the console cat ${{ env.logdir }}BuildErrors.log echo "::group::Build Log" cat ${{ env.logdir }}Build.log echo "::endgroup::" # Exit the step with the exit code of the build if [ ${{ env.buildFailSilent }} != true ]; then exit $exitCode else echo "Build exited with code $exitCode, but we are ignoring it" exit 0 fi - name: Print ccache statistics after Build if: always() run: ccache -s - name: FreeCAD UnitTests id: test if: env.test == 'true' && always() run: | set +e build/bin/FreeCADCmd -t 0 &> ${{ env.logdir }}Test.log exitCode=$? # Trim the Log file # remove "(xx %)" sed -E '/\t+\([[:digit:]]{1,3} %\)\t/d' ${{ env.logdir }}Test.log > ${{ env.logdir }}TestTrimmed.log # Write the test report if [ $exitCode -eq 0 ] ; then echo "
:heavy_check_mark: Unittests succeeded" >> ${{ env.logdir }}checkReport.md else echo "
:fire: CMake test failed" >> ${{ env.logdir }}checkReport.md fi echo "" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md tail -n 100 ${{ env.logdir }}TestTrimmed.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "
">> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md # Print the Log to the console cat ${{ env.logdir }}Test.log # Exit the step with the exit code of the UnitTests if [ ${{ env.testFailSilent }} != true ]; then exit $exitCode else echo "Test exited with code $exitCode, but we are ignoring it" exit 0 fi - name: CMake Install id: install if: env.testInstall == 'true' && always() run: | set +e # Run the CMake Configure step again to update the install path cmake \ -D CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE \ -D CMAKE_INSTALL_PREFIX='/usr/lib/freecad' \ -D CMAKE_INSTALL_BINDIR='/usr/lib/freecad/bin' \ -D CMAKE_INSTALL_LIBDIR='/usr/lib/freecad/lib' \ -D CMAKE_INSTALL_DATADIR='/usr/share/freecad' \ -D CMAKE_INSTALL_DATAROOTDIR='/usr/share' \ -D CMAKE_INSTALL_DOCDIR='/usr/share/doc/freecad' \ -B build/ >> ${{ env.logdir }}Install.log 2>> ${{ env.logdir }}InstallErrors.log # Build again to copy the files to the install path cmake --build build/ >> ${{ env.logdir }}Install.log 2>> ${{ env.logdir }}InstallErrors.log # Run CMake Install sudo cmake --install build/ >> ${{ env.logdir }}Install.log 2>> ${{ env.logdir }}InstallErrors.log exitCode=$? # links for bin FILES=/usr/lib/freecad/bin/* for f in $FILES do sudo ln -vs /usr/lib/freecad/bin/$(basename $f) /usr/bin/$(basename $f) done # Write the install report if [ $exitCode -eq 0 ] ; then echo "
:heavy_check_mark: CMake install succeeded" >> ${{ env.logdir }}checkReport.md else echo "
:fire: CMake install failed" >> ${{ env.logdir }}checkReport.md fi echo "" >> ${{ env.logdir }}checkReport.md echo "Install Error Log (stderr output):" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md cat ${{ env.logdir }}InstallErrors.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "Install Error Log (stdout output trimmed to the last 100 Lines):" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md tail -n 100 ${{ env.logdir }}Install.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "
">> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md # Print the Log to the console cat ${{ env.logdir }}InstallErrors.log echo "::group::Install Log" cat ${{ env.logdir }}Install.log echo "::endgroup::" # Exit the step with the exit code of the Install if [ ${{ env.cmakeInstallFailSilent }} != true ]; then exit $exitCode else echo "CMake install exited with code $exitCode, but we are ignoring it" exit 0 fi - name: FreeCAD UnitTests on Install id: testInstall if: env.testInstall == 'true' && always() run: | set +e LD_LIBRARY_PATH=/usr/lib/freecad/lib FreeCADCmd -t 0 &> ${{ env.logdir }}TestInstall.log exitCode=$? # Trim the Log file # remove "(xx %)" sed -E '/\t+\([[:digit:]]{1,3} %\)\t/d' ${{ env.logdir }}TestInstall.log > ${{ env.logdir }}TestInstallTrimmed.log # Write the testInstall report if [ $exitCode -eq 0 ] ; then echo "
:heavy_check_mark: Unittests on install succeeded" >> ${{ env.logdir }}checkReport.md else echo "
:fire: CMake test on install failed" >> ${{ env.logdir }}checkReport.md fi echo "" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md tail -n 100 ${{ env.logdir }}TestInstallTrimmed.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "
">> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md # Print the Log to the console cat ${{ env.logdir }}TestInstall.log # Exit the step with the exit code of the UnitTests if [ ${{ env.testInstallFailSilent }} != true ]; then exit $exitCode else echo "Test on install exited with code $exitCode, but we are ignoring it" exit 0 fi - name: Report 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 # Write the Log to the console with the Problem Matchers 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 # Write the report if [ $whitespaceErrors -gt 0 ]; then echo "
:information_source: Found $whitespaceErrors trailing whitespace" >> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md cat ${{ 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 # Exit the step with the exit code 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 # Write the Log to the console with the Problem Matchers 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 # Write the report 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 cat ${{ 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 # Exit the step with the exit code 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 cat ${{ 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 # Exit the step with the exit code 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" 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 cat ${{ env.logdir }}cpplint.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "
">> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md # Exit the step with the exit code 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: | set +e pylintErrors="0" pip install pylint pylintErrors="0" pylintWarnings="0" pylintRefactorings="0" pylintConventions="0" # List enabled pylint checks pylint --list-msgs-enabled > ${{ env.logdir }}pylint-enabled-checks.log # Run pylint on all python files pylint --disable=${{ env.pylintDisable }} ${{ steps.files-by-extensions.outputs.python_files }} > ${{ env.logdir }}pylint.log exitCode=$? # 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 # list enabled checks echo "
:information_source: Enabled checks" >> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md cat ${{ env.logdir }}pylint-enabled-checks.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "
">> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md cat ${{ 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 $exitCode else echo "Pylint exited with code $exitCode, but we are ignoring it" exit 0 fi - name: Black (Python) shell: bash if: env.checkBlack == 'true' && steps.files-by-extensions.outputs.python_files != '' && always() run: | set +e pip install black blackReformats="0" blackFails="0" black --check ${{ steps.files-by-extensions.outputs.python_files }} &> ${{ env.logdir }}black.log exitCode=$? # 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 cat ${{ env.logdir }}black.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "
">> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md if [ $exitCode -gt 0 ] && [ ${{ env.blackFailSilent }} != true ]; then exit $exitCode else echo "Black exited with code $exitCode, but we are ignoring it" 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 cat ${{ 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 cat ${{ 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: Clang-tidy shell: bash if: env.checkClangTidy == 'true' && steps.files-by-extensions.outputs.cpp_files != '' && always() run: | set +e clangTidyErrors="0" clangTidyWarnings="0" clangTidyNotes="0" sudo apt-get install -y --no-install-recommends clang-tidy clang-tidy --quiet --format-style=${{ env.clangStyle }} --export-fixes=${{ env.fixesdir }}clang-tidy.yaml -checks=${{ env.clangTidyChecks }} -p build/ --explain-config &>> ${{ env.logdir }}clang-tidy-enabled-checks.log 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 exitCode=$? # 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 # enabled checks echo "
:information_source: Enabled checks" >> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md cat ${{ env.logdir }}clang-tidy-enabled-checks.log >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md echo "
">> ${{ env.logdir }}checkReport.md echo "" >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md cat ${{ 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 echo "Clang-tidy exited with code $exitCode, but we are ignoring it" 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 -y --no-install-recommends 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 # documentation link echo "[List of checks](https://github.com/KDE/clazy#list-of-checks), [This explains some of the clazy warnings](https://www.kdab.com/uncovering-32-qt-best-practices-compile-time-clazy/) " >> ${{ env.logdir }}checkReport.md echo '```' >> ${{ env.logdir }}checkReport.md cat ${{ 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.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 cat ${{ 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' && github.event_name == 'pull_request' && always() uses: marocchino/sticky-pull-request-comment@v2 with: header: Check results path: ${{ env.logdir }}checkReport.md - name: get comment body id: get-comment-body if: env.reportPushComment == 'true' && github.event_name == 'push' && always() run: | body=$(cat ${{ env.logdir }}checkReport.md) body="${body//'%'/'%25'}" body="${body//$'\n'/'%0A'}" body="${body//$'\r'/'%0D'}" echo ::set-output name=body::$body - name: Create commit comment if: env.reportPushComment == 'true' && github.event_name == 'push' && always() uses: peter-evans/commit-comment@v2 with: body: ${{ steps.get-comment-body.outputs.body }}