Add the reusable quality-check workflows for C++ and C# (MIL-007 tasks 4 and 5) #110

Open
Tirsvad wants to merge 6 commits from mil-007-cpp-csharp-workflows into main
7 changed files with 110 additions and 25 deletions
Showing only changes of commit 81866d8dac - Show all commits
+2 -1
View File
@@ -26,7 +26,8 @@ Each release lists its changes in these groups, in this order:
-Werror` and `clang-tidy` on the tracked C and C++ files, at exact versions -Werror` and `clang-tidy` on the tracked C and C++ files, at exact versions
(inputs `clang-format-version`, `clang-tidy-version`), with the inputs (inputs `clang-format-version`, `clang-tidy-version`), with the inputs
`working-directory`, `std`, `compile-commands-dir` and `tools-ref`. The `working-directory`, `std`, `compile-commands-dir` and `tools-ref`. The
repository needs a `.clang-format`. repository needs a `.clang-format`; without a `.clang-tidy` the checks
`clang-diagnostic-*` and `clang-analyzer-*` run.
- Reusable workflow `quality-checks-csharp.yml` (ADR-0008): `dotnet format - Reusable workflow `quality-checks-csharp.yml` (ADR-0008): `dotnet format
--verify-no-changes` and `dotnet build -warnaserror` on an exact .NET SDK 10 --verify-no-changes` and `dotnet build -warnaserror` on an exact .NET SDK 10
or later (input `dotnet-sdk-version`, default `10.0.401`), with the inputs or later (input `dotnet-sdk-version`, default `10.0.401`), with the inputs
+4 -2
View File
@@ -44,7 +44,9 @@ version of a tool is a new release of this repository, not a silent change.
the top of the repository. Without one the run fails with `[configuration]` the top of the repository. Without one the run fails with `[configuration]`
and no tool runs. and no tool runs.
- Optionally a `.clang-tidy` with the checks of the project (for example the - Optionally a `.clang-tidy` with the checks of the project (for example the
C++ Core Guidelines). Without one clang-tidy runs its default checks. C++ Core Guidelines). Without one the workflow runs the compiler diagnostics
and the static analyzer (`clang-diagnostic-*` and `clang-analyzer-*`),
because clang-tidy itself enables no check when there is no configuration.
- For precise static checks, a compile database: build it first (for example - For precise static checks, a compile database: build it first (for example
`cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON`) and pass `cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON`) and pass
`compile-commands-dir: build`. The workflow does not build your project. `compile-commands-dir: build`. The workflow does not build your project.
@@ -54,7 +56,7 @@ version of a tool is a new release of this repository, not a silent change.
| Files | Tool | What it reports | | Files | Tool | What it reports |
| --- | --- | --- | | --- | --- | --- |
| tracked `*.c`, `*.cc`, `*.cpp`, `*.cxx`, `*.h`, `*.hh`, `*.hpp`, `*.hxx` | `clang-format --dry-run -Werror` | formatting that differs from `.clang-format` | | tracked `*.c`, `*.cc`, `*.cpp`, `*.cxx`, `*.h`, `*.hh`, `*.hpp`, `*.hxx` | `clang-format --dry-run -Werror` | formatting that differs from `.clang-format` |
| the same files | `clang-tidy` | findings of the checks of `.clang-tidy`; `*.c` files are checked as C17, the other files as the C++ standard of `std` | | the same files | `clang-tidy` | findings of the checks of `.clang-tidy` (or of `clang-diagnostic-*` and `clang-analyzer-*` without one); `*.c` files are checked as C17, the other files as the C++ standard of `std` |
Untracked files are not checked. A repository without these files passes with a Untracked files are not checked. A repository without these files passes with a
notice. There is no `cppcheck`: only an old version of it is available as a notice. There is no `cppcheck`: only an old version of it is available as a
+64 -1
View File
@@ -5,6 +5,7 @@ network, no clang and no .NET SDK are needed.
""" """
import os import os
import re
import shutil import shutil
import subprocess import subprocess
from pathlib import Path from pathlib import Path
@@ -285,7 +286,69 @@ class TestCppRun:
result, calls = self.run(repo, tmp_path, cpp_bin, COMPILE_COMMANDS_DIR="build") result, calls = self.run(repo, tmp_path, cpp_bin, COMPILE_COMMANDS_DIR="build")
assert result.returncode == 0, result.stdout assert result.returncode == 0, result.stdout
assert len(calls) == 2 assert len(calls) == 2
assert "-p build" in calls[1] and "-std=" not in calls[1] database = re.search(r"-p (\S+)", calls[1])
assert database is not None and database.group(1).endswith(f"/{repo.name}/build")
assert "-std=" not in calls[1]
def test_clang_tidy_gets_default_checks_without_a_clang_tidy_file(
self,
repo: Path,
tmp_path: Path,
cpp_bin: Path,
) -> None:
add_files(repo, "a.cpp")
_, calls = self.run(repo, tmp_path, cpp_bin)
assert "--checks=clang-diagnostic-*,clang-analyzer-*" in calls[1]
assert "--checks=" not in calls[0]
def test_no_default_checks_when_a_clang_tidy_file_is_in_the_repository_root(
self,
repo: Path,
tmp_path: Path,
cpp_bin: Path,
) -> None:
add_files(repo, "a.cpp", ".clang-tidy")
_, calls = self.run(repo, tmp_path, cpp_bin)
assert len(calls) == 2
assert not any("--checks=" in call for call in calls)
def test_no_default_checks_when_a_clang_tidy_file_is_above_the_working_directory(
self,
repo: Path,
tmp_path: Path,
cpp_bin: Path,
) -> None:
add_files(repo, "service/a.cpp", ".clang-tidy")
_, calls = self.run(repo, tmp_path, cpp_bin, WORKING_DIRECTORY="service")
assert len(calls) == 2
assert not any("--checks=" in call for call in calls)
def test_a_compile_database_is_resolved_from_the_repository_root(
self,
repo: Path,
tmp_path: Path,
cpp_bin: Path,
) -> None:
add_files(repo, "service/a.cpp", "build/compile_commands.json")
result, calls = self.run(
repo, tmp_path, cpp_bin, WORKING_DIRECTORY="service", COMPILE_COMMANDS_DIR="build"
)
assert result.returncode == 0, result.stdout
database = re.search(r"-p (\S+)", calls[1])
assert database is not None and database.group(1).endswith(f"/{repo.name}/build")
def test_a_compile_database_only_inside_the_working_directory_is_not_found(
self,
repo: Path,
tmp_path: Path,
cpp_bin: Path,
) -> None:
add_files(repo, "service/a.cpp", "service/build/compile_commands.json")
result, calls = self.run(
repo, tmp_path, cpp_bin, WORKING_DIRECTORY="service", COMPILE_COMMANDS_DIR="build"
)
assert result.returncode == 1 and "[configuration]" in result.stdout
assert calls == []
def test_a_missing_compile_database_is_a_configuration_error( def test_a_missing_compile_database_is_a_configuration_error(
self, self,
+28 -11
View File
@@ -13,6 +13,7 @@
# Every failure is printed as ::error::[category] message. # Every failure is printed as ::error::[category] message.
C_STANDARD="${C_STANDARD:-c17}" C_STANDARD="${C_STANDARD:-c17}"
DEFAULT_TIDY_CHECKS="clang-diagnostic-*,clang-analyzer-*"
validate_inputs() { validate_inputs() {
set -u set -u
@@ -50,16 +51,19 @@ install_tools() {
"clang-format==${CLANG_FORMAT_VERSION}" "clang-tidy==${CLANG_TIDY_VERSION}" "clang-format==${CLANG_FORMAT_VERSION}" "clang-tidy==${CLANG_TIDY_VERSION}"
} }
# A .clang-format must exist in the working directory or above it, up to the # Succeeds when one of the named files exists in the working directory or above
# top of the repository: clang-format finds its style the same way. # it, up to the top of the repository: clang-format and clang-tidy find their
find_clang_format_config() { # configuration the same way.
local top dir parent find_config_upwards() {
local top dir parent name
top="$(cd "$(git rev-parse --show-toplevel)" && pwd -P)" top="$(cd "$(git rev-parse --show-toplevel)" && pwd -P)"
dir="$(pwd -P)" dir="$(pwd -P)"
while true; do while true; do
if [ -f "$dir/.clang-format" ] || [ -f "$dir/_clang-format" ]; then for name in "$@"; do
if [ -f "$dir/$name" ]; then
return 0 return 0
fi fi
done
parent="$(dirname "$dir")" parent="$(dirname "$dir")"
# stop at the top of the repository, or at the file system root # stop at the top of the repository, or at the file system root
if [ "$dir" = "$top" ] || [ "$parent" = "$dir" ]; then if [ "$dir" = "$top" ] || [ "$parent" = "$dir" ]; then
@@ -76,6 +80,12 @@ run_checks() {
echo "::error::[configuration] working-directory '$WORKING_DIRECTORY' does not exist." echo "::error::[configuration] working-directory '$WORKING_DIRECTORY' does not exist."
exit 1 exit 1
fi fi
# compile-commands-dir is relative to the repository root, not to the working directory
local repo_top compile_db=""
repo_top="$(cd "$(git rev-parse --show-toplevel)" && pwd -P)"
if [ -n "$COMPILE_COMMANDS_DIR" ]; then
compile_db="$repo_top/$COMPILE_COMMANDS_DIR"
fi
cd "$WORKING_DIRECTORY" cd "$WORKING_DIRECTORY"
local all_files cpp_files c_files local all_files cpp_files c_files
@@ -85,11 +95,11 @@ run_checks() {
echo "All selected checks passed." echo "All selected checks passed."
exit 0 exit 0
fi fi
if ! find_clang_format_config; then if ! find_config_upwards .clang-format _clang-format; then
echo "::error::[configuration] No .clang-format found in '$WORKING_DIRECTORY' or above it; add the style of the project." echo "::error::[configuration] No .clang-format found in '$WORKING_DIRECTORY' or above it; add the style of the project."
exit 1 exit 1
fi fi
if [ -n "$COMPILE_COMMANDS_DIR" ] && [ ! -f "$COMPILE_COMMANDS_DIR/compile_commands.json" ]; then if [ -n "$compile_db" ] && [ ! -f "$compile_db/compile_commands.json" ]; then
echo "::error::[configuration] compile-commands-dir '$COMPILE_COMMANDS_DIR' has no compile_commands.json; build it before the call." echo "::error::[configuration] compile-commands-dir '$COMPILE_COMMANDS_DIR' has no compile_commands.json; build it before the call."
exit 1 exit 1
fi fi
@@ -115,14 +125,21 @@ run_checks() {
run_tool "clang-format" "$QC_BIN/clang-format" --dry-run -Werror "${all_files[@]}" run_tool "clang-format" "$QC_BIN/clang-format" --dry-run -Werror "${all_files[@]}"
if [ -n "$COMPILE_COMMANDS_DIR" ]; then # Without a .clang-tidy, clang-tidy enables no check and refuses to run, so
run_tool "clang-tidy" "$QC_BIN/clang-tidy" --quiet -p "$COMPILE_COMMANDS_DIR" "${all_files[@]}" # the project gets the compiler diagnostics and the static analyzer.
local tidy_args=(--quiet)
if ! find_config_upwards .clang-tidy _clang-tidy; then
tidy_args+=("--checks=${DEFAULT_TIDY_CHECKS}")
fi
if [ -n "$compile_db" ]; then
run_tool "clang-tidy" "$QC_BIN/clang-tidy" "${tidy_args[@]}" -p "$compile_db" "${all_files[@]}"
else else
if [ "${#cpp_files[@]}" -gt 0 ]; then if [ "${#cpp_files[@]}" -gt 0 ]; then
run_tool "clang-tidy (C++)" "$QC_BIN/clang-tidy" --quiet "${cpp_files[@]}" -- "-std=${STD}" run_tool "clang-tidy (C++)" "$QC_BIN/clang-tidy" "${tidy_args[@]}" "${cpp_files[@]}" -- "-std=${STD}"
fi fi
if [ "${#c_files[@]}" -gt 0 ]; then if [ "${#c_files[@]}" -gt 0 ]; then
run_tool "clang-tidy (C)" "$QC_BIN/clang-tidy" --quiet "${c_files[@]}" -- "-std=${C_STANDARD}" run_tool "clang-tidy (C)" "$QC_BIN/clang-tidy" "${tidy_args[@]}" "${c_files[@]}" -- "-std=${C_STANDARD}"
fi fi
fi fi
+3 -3
View File
@@ -1,4 +1,4 @@
<!-- translation of QualityChecksCpp.md, source sha256 f54f7f7dc713 --> <!-- translation of QualityChecksCpp.md, source sha256 fd8bb0d71906 -->
> Dette er en oversættelse af [`QualityChecksCpp`](QualityChecksCpp). Den engelske side er autoritativ, og ændringer her bliver overskrevet, næste gang wikien publiceres. > Dette er en oversættelse af [`QualityChecksCpp`](QualityChecksCpp). Den engelske side er autoritativ, og ændringer her bliver overskrevet, næste gang wikien publiceres.
@@ -41,7 +41,7 @@ Henvis til workflowet ved major-tagget (`@v1`) eller et præcist tag, aldrig ved
## Hvad repositoryet skal have ## Hvad repositoryet skal have
- En `.clang-format` i arbejdsmappen eller i en mappe over den, op til repositoryets rod. Uden en fejler kørslen med `[configuration]`, og intet værktøj kører. - En `.clang-format` i arbejdsmappen eller i en mappe over den, op til repositoryets rod. Uden en fejler kørslen med `[configuration]`, og intet værktøj kører.
- Eventuelt en `.clang-tidy` med projektets kontroller (for eksempel C++ Core Guidelines). Uden en kører clang-tidy sine standardkontroller. - Eventuelt en `.clang-tidy` med projektets kontroller (for eksempel C++ Core Guidelines). Uden en kører workflowet compilerdiagnostik og den statiske analysator (`clang-diagnostic-*` og `clang-analyzer-*`), fordi clang-tidy selv ikke aktiverer nogen kontrol uden konfiguration.
- For præcise statiske kontroller en compile-database: byg den først (for eksempel `cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON`) og angiv `compile-commands-dir: build`. Workflowet bygger ikke dit projekt. - For præcise statiske kontroller en compile-database: byg den først (for eksempel `cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON`) og angiv `compile-commands-dir: build`. Workflowet bygger ikke dit projekt.
## Hvad der kontrolleres ## Hvad der kontrolleres
@@ -49,7 +49,7 @@ Henvis til workflowet ved major-tagget (`@v1`) eller et præcist tag, aldrig ved
| Filer | Værktøj | Hvad det melder | | Filer | Værktøj | Hvad det melder |
| --- | --- | --- | | --- | --- | --- |
| sporede `*.c`, `*.cc`, `*.cpp`, `*.cxx`, `*.h`, `*.hh`, `*.hpp`, `*.hxx` | `clang-format --dry-run -Werror` | formatering, der afviger fra `.clang-format` | | sporede `*.c`, `*.cc`, `*.cpp`, `*.cxx`, `*.h`, `*.hh`, `*.hpp`, `*.hxx` | `clang-format --dry-run -Werror` | formatering, der afviger fra `.clang-format` |
| de samme filer | `clang-tidy` | fund fra kontrollerne i `.clang-tidy`; `*.c`-filer kontrolleres som C17, de øvrige filer som C++-standarden i `std` | | de samme filer | `clang-tidy` | fund fra kontrollerne i `.clang-tidy` (eller fra `clang-diagnostic-*` og `clang-analyzer-*` uden en); `*.c`-filer kontrolleres som C17, de øvrige filer som C++-standarden i `std` |
Usporede filer kontrolleres ikke. Et repository uden disse filer består med en bemærkning. Der er ingen `cppcheck`: kun en gammel version findes som pakke, og gamle værktøjer køres ikke (ADR-0008). Usporede filer kontrolleres ikke. Et repository uden disse filer består med en bemærkning. Der er ingen `cppcheck`: kun en gammel version findes som pakke, og gamle værktøjer køres ikke (ADR-0008).
+4 -2
View File
@@ -50,7 +50,9 @@ version of a tool is a new release of this repository, not a silent change.
the top of the repository. Without one the run fails with `[configuration]` the top of the repository. Without one the run fails with `[configuration]`
and no tool runs. and no tool runs.
- Optionally a `.clang-tidy` with the checks of the project (for example the - Optionally a `.clang-tidy` with the checks of the project (for example the
C++ Core Guidelines). Without one clang-tidy runs its default checks. C++ Core Guidelines). Without one the workflow runs the compiler diagnostics
and the static analyzer (`clang-diagnostic-*` and `clang-analyzer-*`),
because clang-tidy itself enables no check when there is no configuration.
- For precise static checks, a compile database: build it first (for example - For precise static checks, a compile database: build it first (for example
`cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON`) and pass `cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON`) and pass
`compile-commands-dir: build`. The workflow does not build your project. `compile-commands-dir: build`. The workflow does not build your project.
@@ -60,7 +62,7 @@ version of a tool is a new release of this repository, not a silent change.
| Files | Tool | What it reports | | Files | Tool | What it reports |
| --- | --- | --- | | --- | --- | --- |
| tracked `*.c`, `*.cc`, `*.cpp`, `*.cxx`, `*.h`, `*.hh`, `*.hpp`, `*.hxx` | `clang-format --dry-run -Werror` | formatting that differs from `.clang-format` | | tracked `*.c`, `*.cc`, `*.cpp`, `*.cxx`, `*.h`, `*.hh`, `*.hpp`, `*.hxx` | `clang-format --dry-run -Werror` | formatting that differs from `.clang-format` |
| the same files | `clang-tidy` | findings of the checks of `.clang-tidy`; `*.c` files are checked as C17, the other files as the C++ standard of `std` | | the same files | `clang-tidy` | findings of the checks of `.clang-tidy` (or of `clang-diagnostic-*` and `clang-analyzer-*` without one); `*.c` files are checked as C17, the other files as the C++ standard of `std` |
Untracked files are not checked. A repository without these files passes with a Untracked files are not checked. A repository without these files passes with a
notice. There is no `cppcheck`: only an old version of it is available as a notice. There is no `cppcheck`: only an old version of it is available as a
+3 -3
View File
@@ -1,4 +1,4 @@
<!-- translation of QualityChecksCpp.md, source sha256 f54f7f7dc713 --> <!-- translation of QualityChecksCpp.md, source sha256 fd8bb0d71906 -->
> Ini ialah terjemahan [`QualityChecksCpp`](QualityChecksCpp). Halaman bahasa Inggeris ialah teks yang sah, dan perubahan di sini akan ditimpa pada penerbitan wiki yang seterusnya. > Ini ialah terjemahan [`QualityChecksCpp`](QualityChecksCpp). Halaman bahasa Inggeris ialah teks yang sah, dan perubahan di sini akan ditimpa pada penerbitan wiki yang seterusnya.
@@ -41,7 +41,7 @@ Rujuk aliran kerja pada tag major (`@v1`) atau pada tag tepat, jangan sekali-kal
## Apa yang diperlukan oleh repositori ## Apa yang diperlukan oleh repositori
- Satu `.clang-format` dalam direktori kerja atau dalam direktori di atasnya, sehingga akar repositori. Tanpanya larian gagal dengan `[configuration]` dan tiada alat dijalankan. - Satu `.clang-format` dalam direktori kerja atau dalam direktori di atasnya, sehingga akar repositori. Tanpanya larian gagal dengan `[configuration]` dan tiada alat dijalankan.
- Pilihan: satu `.clang-tidy` dengan semakan projek (contohnya C++ Core Guidelines). Tanpanya clang-tidy menjalankan semakan lalainya. - Pilihan: satu `.clang-tidy` dengan semakan projek (contohnya C++ Core Guidelines). Tanpanya aliran kerja menjalankan diagnostik pengkompil dan penganalisis statik (`clang-diagnostic-*` dan `clang-analyzer-*`), kerana clang-tidy sendiri tidak mengaktifkan sebarang semakan tanpa konfigurasi.
- Untuk semakan statik yang tepat, satu pangkalan data kompilasi: bina dahulu (contohnya `cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON`) dan berikan `compile-commands-dir: build`. Aliran kerja tidak membina projek anda. - Untuk semakan statik yang tepat, satu pangkalan data kompilasi: bina dahulu (contohnya `cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON`) dan berikan `compile-commands-dir: build`. Aliran kerja tidak membina projek anda.
## Apa yang disemak ## Apa yang disemak
@@ -49,7 +49,7 @@ Rujuk aliran kerja pada tag major (`@v1`) atau pada tag tepat, jangan sekali-kal
| Fail | Alat | Apa yang dilaporkan | | Fail | Alat | Apa yang dilaporkan |
| --- | --- | --- | | --- | --- | --- |
| `*.c`, `*.cc`, `*.cpp`, `*.cxx`, `*.h`, `*.hh`, `*.hpp`, `*.hxx` yang dijejaki | `clang-format --dry-run -Werror` | pemformatan yang berbeza daripada `.clang-format` | | `*.c`, `*.cc`, `*.cpp`, `*.cxx`, `*.h`, `*.hh`, `*.hpp`, `*.hxx` yang dijejaki | `clang-format --dry-run -Werror` | pemformatan yang berbeza daripada `.clang-format` |
| fail yang sama | `clang-tidy` | dapatan semakan dalam `.clang-tidy`; fail `*.c` disemak sebagai C17, fail lain sebagai piawaian C++ dalam `std` | | fail yang sama | `clang-tidy` | dapatan semakan dalam `.clang-tidy` (atau `clang-diagnostic-*` dan `clang-analyzer-*` tanpanya); fail `*.c` disemak sebagai C17, fail lain sebagai piawaian C++ dalam `std` |
Fail yang tidak dijejaki tidak disemak. Repositori tanpa fail ini lulus dengan satu notis. Tiada `cppcheck`: hanya versi lama tersedia sebagai pakej, dan alat lama tidak dijalankan (ADR-0008). Fail yang tidak dijejaki tidak disemak. Repositori tanpa fail ini lulus dengan satu notis. Tiada `cppcheck`: hanya versi lama tersedia sebagai pakej, dan alat lama tidak dijalankan (ADR-0008).