Diferență între revizuiri ale paginii „SDPT Lab 7”

De la WikiLabs
Jump to navigationJump to search
(Pagină nouă: = Week 7 Lab Activity: Adding a Static Analysis Quality Gate = == Introduction == In Week 6, you built a CI pipeline that automatically compiles and tests your Oven Controller co...)
 
 
(Nu s-a afișat o versiune intermediară efectuată de același utilizator)
Linia 1: Linia 1:
 
= Week 7 Lab Activity: Adding a Static Analysis Quality Gate =
 
= Week 7 Lab Activity: Adding a Static Analysis Quality Gate =
  
== Introduction ==
+
== Objective ==
  
In Week 6, you built a CI pipeline that automatically compiles and tests your Oven Controller code on every push. Today you will add a new stage that runs '''before''' the build: a <code>lint</code> stage that uses static analysis to catch entire classes of bugs that the compiler and the unit tests cannot.
+
Extend your Week 6 CI pipeline with a new <code>lint</code> stage that runs '''before''' the build and blocks Merge Requests on static analysis findings. By the end of this lab your pipeline gates code on three things: it must lint cleanly, it must compile, and it must pass tests.
  
By the end of this lab, you will have:
+
== Background ==
  
# Extended your Docker build environment to include <code>cppcheck</code> and <code>clang-tidy</code>.
+
In Week 6 you built a CI pipeline with at least <code>build</code> and <code>test</code> stages. Today you are adding a <code>lint</code> stage as the first stage. A failure there skips later stages and blocks the MR.
# Created a <code>.clang-tidy</code> configuration file at the root of your project.
 
# Hooked clang-tidy into every CMake compile via <code>CMAKE_CXX_CLANG_TIDY</code>.
 
# Added a new <code>lint</code> stage to your <code>.gitlab-ci.yml</code> that runs cppcheck.
 
# Verified that introducing a deliberate bug causes the pipeline to fail and block a Merge Request.
 
# Fixed the deliberate bug and watched the pipeline turn green again.
 
  
== Prerequisites ==
+
You will use two complementary tools. '''cppcheck''' is a fast, pattern-based analyzer that runs from the command line on your source tree without needing your build to succeed; its strength is speed and a low false-positive rate. '''clang-tidy''' is a deeper AST-based analyzer built on top of Clang, which means it understands templates, overloads, lifetimes, and modern C++ semantics. It ships hundreds of configurable checks grouped into families such as <code>bugprone-*</code>, <code>cert-*</code>, <code>cppcoreguidelines-*</code>, <code>modernize-*</code>, <code>performance-*</code>, and <code>readability-*</code>. The full catalog is at https://clang.llvm.org/extra/clang-tidy/checks/list.html.
  
You should be starting from your finished Week 6 project, which has:
+
clang-tidy needs to know exactly how each file is compiled (include paths, defines, C++ standard). CMake produces that information for free if you set <code>CMAKE_EXPORT_COMPILE_COMMANDS=ON</code>, which writes a <code>compile_commands.json</code> to your build directory. To make clang-tidy run automatically on every compile, set the CMake variable <code>CMAKE_CXX_CLANG_TIDY</code> to a clang-tidy command line --- CMake will then invoke clang-tidy alongside the compiler on every C++ source file. To run cppcheck from a build target, wrap it in an <code>add_custom_target</code> definition.
  
* A working <code>CMakeLists.txt</code> for the Oven Controller.
+
clang-tidy is configured by a <code>.clang-tidy</code> YAML file at the repo root. The two important keys for this lab are <code>Checks</code> (a comma-separated glob list, prefix <code>-</code> to disable) and <code>WarningsAsErrors</code> (same syntax). The conventional pattern is to start with <code>-*</code> and then re-enable only the families you want, rather than enabling everything and trying to silence the noise.
* A passing Google Test suite.
 
* A <code>Dockerfile</code> producing a multi-arch build environment.
 
* A <code>.gitlab-ci.yml</code> with at least <code>build</code> and <code>test</code> stages running on a custom GitLab Runner.
 
  
If any of those is missing or broken, fix it before continuing. Static analysis added on top of a broken pipeline does not help anyone.
+
Both tools support inline suppressions: <code>// cppcheck-suppress &lt;id&gt;</code> and <code>// NOLINTNEXTLINE(&lt;check&gt;)</code>. Always pair a suppression with a comment explaining ''why''. Unjustified suppressions are themselves a code smell.
  
== Step 1: Create a Feature Branch ==
+
For tool usage refer to <code>cppcheck --help</code>, <code>man cppcheck</code>, <code>clang-tidy --help</code>, and the check catalog linked above.
  
As always, do '''not''' work on <code>main</code>. Open an Issue first ("Add static analysis quality gate") and then create a feature branch:
+
== Tasks ==
  
<pre>
+
Work on a feature branch opened from a tracked Issue. Open a Merge Request when you are ready to integrate.
git checkout main
 
git pull
 
git checkout -b feature/static-analysis
 
</pre>
 
  
This branch will contain everything you do in this lab. At the end you will open a Merge Request just like in Week 2.
+
# '''Extend the build environment.''' Update your <code>Dockerfile</code> so the image installs both <code>cppcheck</code> and <code>clang-tidy</code>. Rebuild the image and verify both binaries respond to <code>--version</code> from inside a container. If your runner pulls the image from a registry, push the new tag.
 +
# '''Configure clang-tidy.''' Create <code>.clang-tidy</code> at the repository root. You must enable, at a minimum, the entire <code>bugprone-*</code> family and at least two specific checks from <code>cppcoreguidelines-*</code>. You must explicitly disable any check from those families that fires noisily on your existing code (e.g. some <code>readability-identifier-length</code> or <code>cppcoreguidelines-pro-bounds-*</code> checks are commonly muted in embedded code). Set <code>WarningsAsErrors</code> so any reported issue fails the build. Add a comment block at the top of the file explaining the rationale for your check selection --- this is part of the deliverable.
 +
# '''Wire clang-tidy into CMake.''' Modify <code>CMakeLists.txt</code> so that compile commands are exported and clang-tidy runs automatically on every C++ compile. Make sure the build still works for team members who do not have clang-tidy installed locally (hint: <code>find_program</code> can return a NOTFOUND value, and you can branch on that).
 +
# '''Add a cppcheck custom target.''' Define a CMake custom target named <code>cppcheck</code> that runs the tool on your source tree. Required flags: at minimum <code>--enable=warning,style,performance,portability</code>, <code>--error-exitcode=1</code> (so it propagates failures), <code>--inline-suppr</code> (so your inline suppressions are honored), and <code>--std=c++17</code> (or whatever your project uses). Decide for yourself whether to scan or skip the build directory, the test directory, and any third-party code.
 +
# '''Add the lint stage to GitLab CI.''' Edit <code>.gitlab-ci.yml</code> to add a new <code>lint</code> stage as the first stage. Add a job that runs cppcheck inside your updated Docker image. The job must fail the pipeline on any cppcheck finding. clang-tidy itself does '''not''' need a separate CI job, since wiring it into <code>CMAKE_CXX_CLANG_TIDY</code> means it already runs during your existing build job.
 +
# '''Clean up your existing code.''' Push your branch and let the pipeline run. If clang-tidy or cppcheck flags real issues in the Oven Controller, fix them properly. If you genuinely believe a finding is a false positive, suppress it inline with a justification comment --- but no more than two suppressions for this lab. Three or more suppressions almost always means you should fix the underlying problem instead.
 +
# '''Verify the gate fires.''' Once the pipeline is fully green, introduce a deliberate bug on your branch. Pick something the tools should catch: a memory leak, a null pointer dereference, an uninitialized read, a forgotten <code>delete[]</code>, a use-after-move, a buffer overflow. Push the bug, confirm the <code>lint</code> or <code>build</code> stage fails, then preserve a copy of the relevant tool output (a job-log permalink in the MR description is sufficient). Revert the deliberate bug; your final pipeline must be green.
 +
# '''Measure the cost.''' Compare the wall-clock time of your full pipeline before and after this lab. Record both numbers and the percentage increase in the MR description, along with one sentence on whether the trade-off is worth it for this codebase. Be honest --- "the lint stage adds 4 minutes and we caught zero real bugs today" is a valid finding.
 +
# '''Review and merge.''' Open the MR and have a teammate review it. The reviewer should specifically check: the rationale comment in <code>.clang-tidy</code>, that any suppression has a justification, the visibility of the deliberate-bug pipeline run, and the cost measurement. Merge when approved.
  
== Step 2: Update the Dockerfile ==
+
== Stretch tasks (optional, if you finish early) ==
  
Open your <code>Dockerfile</code> and add <code>cppcheck</code> and <code>clang-tidy</code> to the <code>apt-get install</code> line.
+
Pick at most one. These will not gain you points but will make your project measurably more professional.
  
<pre>
+
* '''Tighten the rules.''' Add a check from <code>cert-*</code> or <code>clang-analyzer-*</code> and resolve all findings.
RUN apt-get update && apt-get install -y \
+
* '''Explore MISRA.''' cppcheck ships an experimental <code>misra</code> addon. Run it against your code (you may need a rules text file --- see the cppcheck addons documentation), and report the top three findings together with the MISRA rule numbers they correspond to.
    build-essential \
+
* '''Add a pre-commit hook.''' Configure either a Git pre-commit hook or the <code>pre-commit</code> framework to run cppcheck on staged files. This catches issues before they reach CI and saves runner time.
    cmake \
+
* '''Generate a SARIF report.''' Configure clang-tidy to emit findings in SARIF format and upload it as a GitLab CI artifact. This is how findings are typically aggregated across larger projects.
    g++-aarch64-linux-gnu \
 
    qemu-user-static \
 
    libgtest-dev \
 
    cppcheck \
 
    clang-tidy \
 
&& rm -rf /var/lib/apt/lists/*
 
</pre>
 
 
 
Rebuild the image locally to make sure it still works:
 
 
 
<pre>
 
docker build -t oven-build:lint-test .
 
docker run --rm oven-build:lint-test cppcheck --version
 
docker run --rm oven-build:lint-test clang-tidy --version
 
</pre>
 
 
 
You should see version strings for both tools. If either command is "not found," double-check the package names for your base image (Debian/Ubuntu use <code>cppcheck</code> and <code>clang-tidy</code>; on minimal images you may need <code>clang-tools</code> instead of <code>clang-tidy</code>).
 
 
 
== Step 3: Create the .clang-tidy Configuration File ==
 
 
 
In the root of your repository (next to <code>CMakeLists.txt</code>), create a new file named exactly <code>.clang-tidy</code> (note the leading dot). Paste the following:
 
 
 
<pre>
 
---
 
Checks: >
 
  -*,
 
  bugprone-*,
 
  cert-*,
 
  clang-analyzer-*,
 
  cppcoreguidelines-pro-*,
 
  cppcoreguidelines-slicing,
 
  misc-unused-*,
 
  modernize-use-nullptr,
 
  modernize-use-override,
 
  modernize-use-nodiscard,
 
  performance-*,
 
  portability-*,
 
  readability-braces-around-statements,
 
  readability-misleading-indentation,
 
  readability-redundant-*,
 
  -bugprone-easily-swappable-parameters,
 
  -cppcoreguidelines-pro-bounds-pointer-arithmetic
 
 
 
WarningsAsErrors: '*'
 
HeaderFilterRegex: '.*'
 
FormatStyle: 'file'
 
</pre>
 
 
 
A few notes on what this configuration does:
 
 
 
* The first line <code>-*</code> '''disables''' every check, then we re-enable specific families. This is the safer approach because clang-tidy ships hundreds of checks and many are too noisy.
 
* <code>WarningsAsErrors: '*'</code> turns every remaining warning into an error. The pipeline will fail if any check fires.
 
* <code>HeaderFilterRegex: '.*'</code> tells clang-tidy to also analyze the project's headers (otherwise it skips them).
 
* The two trailing <code>-...</code> entries '''suppress''' specific checks that produce too many false positives in embedded code.
 
 
 
Commit this file:
 
 
 
<pre>
 
git add .clang-tidy
 
git commit -m "Add .clang-tidy configuration"
 
</pre>
 
 
 
== Step 4: Hook clang-tidy into CMake ==
 
 
 
Open <code>CMakeLists.txt</code> and add the following near the top, '''after''' the <code>project(...)</code> line but '''before''' any <code>add_executable</code> or <code>add_library</code> calls:
 
 
 
<pre>
 
# Generate compile_commands.json for tooling
 
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 
 
 
# Enable clang-tidy on every compile
 
find_program(CLANG_TIDY_EXE NAMES clang-tidy)
 
if(CLANG_TIDY_EXE)
 
    set(CMAKE_CXX_CLANG_TIDY
 
        ${CLANG_TIDY_EXE}
 
        --config-file=${CMAKE_SOURCE_DIR}/.clang-tidy)
 
    message(STATUS "clang-tidy enabled: ${CLANG_TIDY_EXE}")
 
else()
 
    message(WARNING "clang-tidy not found; lint disabled")
 
endif()
 
</pre>
 
 
 
The reason we wrap it in <code>if(CLANG_TIDY_EXE)</code> is so that team members who do not have clang-tidy installed locally can still build. The CI environment '''does''' have it, so the pipeline will still enforce it.
 
 
 
Test it locally:
 
 
 
<pre>
 
rm -rf build
 
docker run --rm -v $PWD:/work -w /work oven-build:lint-test \
 
    bash -c "cmake -B build && cmake --build build"
 
</pre>
 
 
 
You should see <code>-- clang-tidy enabled: /usr/bin/clang-tidy</code> in the configure step, and during the build clang-tidy runs alongside the compiler. If your code is clean, the build still succeeds. If clang-tidy finds something, the build fails with a clang-tidy error message.
 
 
 
== Step 5: Add a Cppcheck Custom Target ==
 
 
 
cppcheck does not need CMake to drive it (it parses source directly), but adding a CMake target makes it convenient to run locally. Append this to <code>CMakeLists.txt</code>:
 
 
 
<pre>
 
# Cppcheck target (optional locally, mandatory in CI)
 
find_program(CPPCHECK_EXE NAMES cppcheck)
 
if(CPPCHECK_EXE)
 
    add_custom_target(cppcheck
 
        COMMAND ${CPPCHECK_EXE}
 
            --enable=warning,style,performance,portability
 
            --inline-suppr
 
            --error-exitcode=1
 
            --suppress=missingIncludeSystem
 
            --std=c++17
 
            -I ${CMAKE_SOURCE_DIR}/include
 
            ${CMAKE_SOURCE_DIR}/src
 
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
 
        COMMENT "Running cppcheck on src/")
 
endif()
 
</pre>
 
 
 
Adjust the <code>-I</code> include path and the source path to match your project layout if it differs.
 
 
 
You can now run cppcheck locally with:
 
 
 
<pre>
 
cmake --build build --target cppcheck
 
</pre>
 
 
 
If your code is clean, the command prints nothing and exits with status 0.
 
 
 
Commit your CMake changes:
 
 
 
<pre>
 
git add CMakeLists.txt Dockerfile
 
git commit -m "Wire clang-tidy and cppcheck into the build"
 
</pre>
 
 
 
== Step 6: Add the lint Stage to GitLab CI ==
 
 
 
Open <code>.gitlab-ci.yml</code>. Add <code>lint</code> as the '''first''' stage in your <code>stages:</code> list:
 
 
 
<pre>
 
stages:
 
  - lint
 
  - build
 
  - test
 
</pre>
 
 
 
Then add a new job that runs cppcheck. Place it above your existing <code>build</code> job:
 
 
 
<pre>
 
cppcheck:
 
  stage: lint
 
  image: $CI_REGISTRY_IMAGE/oven-build:latest
 
  script:
 
    - cppcheck
 
        --enable=warning,style,performance,portability
 
        --inline-suppr
 
        --error-exitcode=1
 
        --suppress=missingIncludeSystem
 
        --std=c++17
 
        -I include
 
        src
 
</pre>
 
 
 
Note: clang-tidy is '''not''' a separate CI job. Because we wired it into <code>CMAKE_CXX_CLANG_TIDY</code>, it runs during the existing <code>build</code> job, on every translation unit. If clang-tidy reports an error, the build job fails just as if the compiler had failed. This is exactly the behavior we want: one pipeline run, multiple gates.
 
 
 
Commit and push your branch:
 
 
 
<pre>
 
git add .gitlab-ci.yml
 
git commit -m "Add lint stage running cppcheck"
 
git push -u origin feature/static-analysis
 
</pre>
 
 
 
Open a Merge Request from <code>feature/static-analysis</code> into <code>main</code>. The pipeline should run, the new <code>lint</code> stage should appear, and (assuming your code is clean) all stages should pass.
 
 
 
== Step 7: Trigger a Failure on Purpose ==
 
 
 
Quality gates are only useful if they actually catch things. Let us prove ours does.
 
 
 
Pick any <code>.cpp</code> file in your project --- for example <code>src/OvenController.cpp</code>. Add the following intentionally broken function near the top of the file, '''but inside the same namespace and class''' so it actually compiles:
 
 
 
<pre>
 
// Intentional bug for Week 7 lab. Remove before merge.
 
void OvenController::leakyDiagnostic() {
 
    int* readings = new int[100];
 
    if (sensors_.empty()) {
 
        return;  // <-- LEAK: never delete[] readings
 
    }
 
    for (int i = 0; i < 100; ++i) {
 
        readings[i] = i;
 
    }
 
    delete[] readings;
 
}
 
</pre>
 
 
 
Also declare it in the corresponding header. Commit and push:
 
 
 
<pre>
 
git add src/OvenController.cpp include/OvenController.hpp
 
git commit -m "Add deliberate leak (lab demo, will revert)"
 
git push
 
</pre>
 
 
 
Now watch your Merge Request. The new pipeline run should:
 
 
 
# Start the <code>lint</code> stage.
 
# cppcheck reports the leak and exits non-zero.
 
# The <code>lint</code> job turns red.
 
# The <code>build</code> and <code>test</code> jobs are skipped (because <code>lint</code> failed).
 
# The Merge Request is automatically blocked from merging.
 
 
 
Click into the failed job and read the cppcheck output. You should see something close to:
 
 
 
<pre>
 
src/OvenController.cpp:42:5: error: Memory leak: readings [memleak]
 
</pre>
 
 
 
This is exactly what should happen. The pipeline did its job.
 
 
 
== Step 8: Fix and Merge ==
 
 
 
Revert the deliberate bug:
 
 
 
<pre>
 
git revert HEAD
 
git push
 
</pre>
 
 
 
(Or remove the function manually and commit. Either is fine.)
 
 
 
The new pipeline should pass all stages: <code>lint</code> green, <code>build</code> green (with clang-tidy clean), <code>test</code> green. Merge the MR via GitLab.
 
 
 
You now have a working, multi-stage CI pipeline that enforces both unit tests '''and''' static analysis on every change to the codebase.
 
  
 
== Deliverables ==
 
== Deliverables ==
  
To be marked as complete, your <code>main</code> branch must contain:
+
For this lab to be marked complete, your <code>main</code> branch must contain:
 
 
# An updated <code>Dockerfile</code> with <code>cppcheck</code> and <code>clang-tidy</code> installed.
 
# A <code>.clang-tidy</code> configuration file at the repo root.
 
# A <code>CMakeLists.txt</code> that exports <code>compile_commands.json</code>, sets <code>CMAKE_CXX_CLANG_TIDY</code>, and defines a <code>cppcheck</code> custom target.
 
# A <code>.gitlab-ci.yml</code> with a <code>lint</code> stage running cppcheck '''before''' the <code>build</code> stage.
 
# A merged Merge Request whose pipeline shows '''four''' green jobs (lint, build, test, plus any others you already had).
 
# Evidence in the MR's pipeline history of '''at least one failed pipeline''' caused by the deliberate bug, followed by the fix.
 
 
 
== Common Issues ==
 
 
 
''clang-tidy reports many warnings on third-party headers (Google Test, system includes).''
 
* Make sure <code>HeaderFilterRegex</code> in <code>.clang-tidy</code> only matches your own headers, e.g. <code>'^src/|^include/'</code>.
 
 
 
''cppcheck complains about missing system headers.''
 
* That is what <code>--suppress=missingIncludeSystem</code> is for. Make sure you kept that flag.
 
  
''clang-tidy fails because it cannot find compile flags.''
+
# A <code>Dockerfile</code> that installs both static analysis tools.
* Confirm that <code>set(CMAKE_EXPORT_COMPILE_COMMANDS ON)</code> is in <code>CMakeLists.txt</code>, that <code>build/compile_commands.json</code> exists after configure, and that you ran CMake before clang-tidy.
+
# A <code>.clang-tidy</code> file with a rationale comment and your chosen check set.
 +
# A <code>CMakeLists.txt</code> that exports compile commands, hooks clang-tidy into the build, and defines a <code>cppcheck</code> custom target.
 +
# A <code>.gitlab-ci.yml</code> with a <code>lint</code> stage running before the build stage.
 +
# A merged MR whose linked Issue (or MR description) contains:
 +
## The cost measurement from Task 8.
 +
## A reference to the failed pipeline from Task 7 and the green pipeline that followed.
 +
## A justification for any inline suppression added during Task 6.
  
''The pipeline runs forever, then times out.''
+
== Common pitfalls ==
* clang-tidy is significantly slower than the compiler. If a full build now takes more than 10 minutes, consider narrowing your <code>Checks:</code> list. Start tight, loosen as you learn which checks pull their weight.
 
  
''My capstone project codebase already has dozens of warnings.''
+
* '''clang-tidy reporting findings in third-party headers''' (Google Test, system headers, etc.). Constrain <code>HeaderFilterRegex</code> in <code>.clang-tidy</code> to match only your own paths.
* That is normal for an existing codebase. Two strategies: (1) fix them all in one MR, or (2) configure clang-tidy to warn-but-not-fail until you have cleaned things up. Option (1) is cleaner. Option (2) is realistic when you inherit legacy code.
+
* '''cppcheck warning about missing system headers.''' Add <code>--suppress=missingIncludeSystem</code> to your invocation.
 +
* '''clang-tidy producing different findings on different machines.''' Almost always means <code>compile_commands.json</code> is missing or stale --- regenerate it after every CMake configure, and make sure the file is inside the build directory clang-tidy looks at.
 +
* '''The pipeline timing out.''' clang-tidy is significantly slower than the compiler. If the full build now exceeds your runner's timeout, narrow your check set or scope <code>HeaderFilterRegex</code> more tightly.
 +
* '''"It works on my machine but fails in CI."''' Almost always means your locally installed clang-tidy version differs from the one in the Docker image. The container is the source of truth.
 +
* '''Suppressing checks instead of fixing code.''' If you find yourself adding a third suppression, stop and reconsider. The right action is almost always to fix the code, not to silence the tool.
  
== Looking Ahead ==
+
== Looking ahead ==
  
Next week we leave the CI pipeline alone and turn to the '''system architecture''' of your capstone: how does your Raspberry Pi actually talk to the VM server? Sockets, REST, MQTT, and the design trade-offs between them.
+
Next week we leave the CI pipeline alone for one session and focus on the architecture of your capstone: how the Raspberry Pi and the VM server actually communicate. Bring your design questions about the project.

Versiunea curentă din 27 aprilie 2026 00:11

Week 7 Lab Activity: Adding a Static Analysis Quality Gate

Objective

Extend your Week 6 CI pipeline with a new lint stage that runs before the build and blocks Merge Requests on static analysis findings. By the end of this lab your pipeline gates code on three things: it must lint cleanly, it must compile, and it must pass tests.

Background

In Week 6 you built a CI pipeline with at least build and test stages. Today you are adding a lint stage as the first stage. A failure there skips later stages and blocks the MR.

You will use two complementary tools. cppcheck is a fast, pattern-based analyzer that runs from the command line on your source tree without needing your build to succeed; its strength is speed and a low false-positive rate. clang-tidy is a deeper AST-based analyzer built on top of Clang, which means it understands templates, overloads, lifetimes, and modern C++ semantics. It ships hundreds of configurable checks grouped into families such as bugprone-*, cert-*, cppcoreguidelines-*, modernize-*, performance-*, and readability-*. The full catalog is at https://clang.llvm.org/extra/clang-tidy/checks/list.html.

clang-tidy needs to know exactly how each file is compiled (include paths, defines, C++ standard). CMake produces that information for free if you set CMAKE_EXPORT_COMPILE_COMMANDS=ON, which writes a compile_commands.json to your build directory. To make clang-tidy run automatically on every compile, set the CMake variable CMAKE_CXX_CLANG_TIDY to a clang-tidy command line --- CMake will then invoke clang-tidy alongside the compiler on every C++ source file. To run cppcheck from a build target, wrap it in an add_custom_target definition.

clang-tidy is configured by a .clang-tidy YAML file at the repo root. The two important keys for this lab are Checks (a comma-separated glob list, prefix - to disable) and WarningsAsErrors (same syntax). The conventional pattern is to start with -* and then re-enable only the families you want, rather than enabling everything and trying to silence the noise.

Both tools support inline suppressions: // cppcheck-suppress <id> and // NOLINTNEXTLINE(<check>). Always pair a suppression with a comment explaining why. Unjustified suppressions are themselves a code smell.

For tool usage refer to cppcheck --help, man cppcheck, clang-tidy --help, and the check catalog linked above.

Tasks

Work on a feature branch opened from a tracked Issue. Open a Merge Request when you are ready to integrate.

  1. Extend the build environment. Update your Dockerfile so the image installs both cppcheck and clang-tidy. Rebuild the image and verify both binaries respond to --version from inside a container. If your runner pulls the image from a registry, push the new tag.
  2. Configure clang-tidy. Create .clang-tidy at the repository root. You must enable, at a minimum, the entire bugprone-* family and at least two specific checks from cppcoreguidelines-*. You must explicitly disable any check from those families that fires noisily on your existing code (e.g. some readability-identifier-length or cppcoreguidelines-pro-bounds-* checks are commonly muted in embedded code). Set WarningsAsErrors so any reported issue fails the build. Add a comment block at the top of the file explaining the rationale for your check selection --- this is part of the deliverable.
  3. Wire clang-tidy into CMake. Modify CMakeLists.txt so that compile commands are exported and clang-tidy runs automatically on every C++ compile. Make sure the build still works for team members who do not have clang-tidy installed locally (hint: find_program can return a NOTFOUND value, and you can branch on that).
  4. Add a cppcheck custom target. Define a CMake custom target named cppcheck that runs the tool on your source tree. Required flags: at minimum --enable=warning,style,performance,portability, --error-exitcode=1 (so it propagates failures), --inline-suppr (so your inline suppressions are honored), and --std=c++17 (or whatever your project uses). Decide for yourself whether to scan or skip the build directory, the test directory, and any third-party code.
  5. Add the lint stage to GitLab CI. Edit .gitlab-ci.yml to add a new lint stage as the first stage. Add a job that runs cppcheck inside your updated Docker image. The job must fail the pipeline on any cppcheck finding. clang-tidy itself does not need a separate CI job, since wiring it into CMAKE_CXX_CLANG_TIDY means it already runs during your existing build job.
  6. Clean up your existing code. Push your branch and let the pipeline run. If clang-tidy or cppcheck flags real issues in the Oven Controller, fix them properly. If you genuinely believe a finding is a false positive, suppress it inline with a justification comment --- but no more than two suppressions for this lab. Three or more suppressions almost always means you should fix the underlying problem instead.
  7. Verify the gate fires. Once the pipeline is fully green, introduce a deliberate bug on your branch. Pick something the tools should catch: a memory leak, a null pointer dereference, an uninitialized read, a forgotten delete[], a use-after-move, a buffer overflow. Push the bug, confirm the lint or build stage fails, then preserve a copy of the relevant tool output (a job-log permalink in the MR description is sufficient). Revert the deliberate bug; your final pipeline must be green.
  8. Measure the cost. Compare the wall-clock time of your full pipeline before and after this lab. Record both numbers and the percentage increase in the MR description, along with one sentence on whether the trade-off is worth it for this codebase. Be honest --- "the lint stage adds 4 minutes and we caught zero real bugs today" is a valid finding.
  9. Review and merge. Open the MR and have a teammate review it. The reviewer should specifically check: the rationale comment in .clang-tidy, that any suppression has a justification, the visibility of the deliberate-bug pipeline run, and the cost measurement. Merge when approved.

Stretch tasks (optional, if you finish early)

Pick at most one. These will not gain you points but will make your project measurably more professional.

  • Tighten the rules. Add a check from cert-* or clang-analyzer-* and resolve all findings.
  • Explore MISRA. cppcheck ships an experimental misra addon. Run it against your code (you may need a rules text file --- see the cppcheck addons documentation), and report the top three findings together with the MISRA rule numbers they correspond to.
  • Add a pre-commit hook. Configure either a Git pre-commit hook or the pre-commit framework to run cppcheck on staged files. This catches issues before they reach CI and saves runner time.
  • Generate a SARIF report. Configure clang-tidy to emit findings in SARIF format and upload it as a GitLab CI artifact. This is how findings are typically aggregated across larger projects.

Deliverables

For this lab to be marked complete, your main branch must contain:

  1. A Dockerfile that installs both static analysis tools.
  2. A .clang-tidy file with a rationale comment and your chosen check set.
  3. A CMakeLists.txt that exports compile commands, hooks clang-tidy into the build, and defines a cppcheck custom target.
  4. A .gitlab-ci.yml with a lint stage running before the build stage.
  5. A merged MR whose linked Issue (or MR description) contains:
    1. The cost measurement from Task 8.
    2. A reference to the failed pipeline from Task 7 and the green pipeline that followed.
    3. A justification for any inline suppression added during Task 6.

Common pitfalls

  • clang-tidy reporting findings in third-party headers (Google Test, system headers, etc.). Constrain HeaderFilterRegex in .clang-tidy to match only your own paths.
  • cppcheck warning about missing system headers. Add --suppress=missingIncludeSystem to your invocation.
  • clang-tidy producing different findings on different machines. Almost always means compile_commands.json is missing or stale --- regenerate it after every CMake configure, and make sure the file is inside the build directory clang-tidy looks at.
  • The pipeline timing out. clang-tidy is significantly slower than the compiler. If the full build now exceeds your runner's timeout, narrow your check set or scope HeaderFilterRegex more tightly.
  • "It works on my machine but fails in CI." Almost always means your locally installed clang-tidy version differs from the one in the Docker image. The container is the source of truth.
  • Suppressing checks instead of fixing code. If you find yourself adding a third suppression, stop and reconsider. The right action is almost always to fix the code, not to silence the tool.

Looking ahead

Next week we leave the CI pipeline alone for one session and focus on the architecture of your capstone: how the Raspberry Pi and the VM server actually communicate. Bring your design questions about the project.