Advanced CI/CD for C++ Projects on GitHub Actions
When a simple "build — run tests — deploy" pipeline is no longer sufficient, it's time for advanced techniques. In this article, we'll look at how to configure GitHub Actions for C++ projects at a professional level: dependency caching, build matrix for different compilers, static code analysis, artifacts, and smart triggers. All examples are given in C++ using CMake.
1. Dependency Caching and Build Acceleration
Each workflow run starts with a clean virtual machine. Without caching, you will download and build the same library (e.g., Boost or Google Test) every time. The solution is to use built-in GitHub Actions caching.
name: CI for C++ (advanced)on: [push, pull_request]
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Cache dependencies uses: actions/cache@v3 with: path: | ~/.cache build/_deps key: ${{ runner.os }}-c++-${{ hashFiles('CMakeLists.txt', '**/*.cmake') }} restore-keys: | ${{ runner.os }}-c++-
- name: Install system dependencies run: sudo apt-get update && sudo apt-get install -y libboost-all-dev
- name: Build run: | cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build -j$(nproc)The key is formed based on the hash of CMakeLists.txt — when the dependency list changes, the cache is automatically invalidated. The restore-keys parameter allows partial cache restoration if an exact key is not found.
2. Build Matrix: Multiple Compilers and Platforms
When developing a cross-platform C++ application, it's important to verify the build under different compilers. GitHub Actions supports a matrix strategy.
jobs: build: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] compiler: [gcc, clang, msvc] exclude: - os: ubuntu-latest compiler: msvc - os: windows-latest compiler: clang - os: macos-latest compiler: msvc
runs-on: ${{ matrix.os }}
steps: - uses: actions/checkout@v4
- name: Configure compiler (Linux) if: runner.os == 'Linux' run: | if [ "${{ matrix.compiler }}" == "gcc" ]; then echo "CC=gcc" >> $GITHUB_ENV echo "CXX=g++" >> $GITHUB_ENV elif [ "${{ matrix.compiler }}" == "clang" ]; then echo "CC=clang" >> $GITHUB_ENV echo "CXX=clang++" >> $GITHUB_ENV fi
- name: Build with CMake run: | cmake -B build -DCMAKE_BUILD_TYPE=Debug cmake --build buildThe matrix automatically generates 6 jobs (3 OS × 2 compilers after excluding invalid combinations). This allows you to quickly detect issues specific to a particular compiler or platform.
3. Static Analysis and Linters
C++ code quality is critical. Integrate static analysis into your pipeline using Clang-Tidy, Cppcheck, or SonarCloud. Let's look at an example with Clang-Tidy.
jobs: static-analysis: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Install tools run: | sudo apt-get install -y clang-tidy cppcheck pip install compiledb
- name: Generate compile_commands.json run: | cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON compiledb -o build/compile_commands.json make -C build
- name: Run Clang-Tidy run: | find . -name '*.cpp' -o -name '*.hpp' | xargs clang-tidy -p build --checks='*,-modernize-use