Giao diện
🔄 CI/CD với GitHub Actions Production
Automate everything — Build, Test, và Deploy C++ projects trên mọi platform với GitHub Actions.
Tại sao CI/CD cho C++?
┌─────────────────────────────────────────────────────────────────────────┐
│ CI/CD FOR C++ CODEBASES │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Không có CI: │
│ ───────────── │
│ • "Works on my machine" syndrome │
│ • Bugs slip vào main branch │
│ • Release = 3-day manual process │
│ • Platform-specific bugs phát hiện bởi customers │
│ │
│ Có CI/CD: │
│ ────────── │
│ • Every PR tested on Ubuntu + Windows + macOS │
│ • Sanitizers catch memory bugs trước merge │
│ • Auto-release với version tagging │
│ • Binary artifacts for each platform │
│ │
└─────────────────────────────────────────────────────────────────────────┘Basic CMake Workflow
Tạo file .github/workflows/cmake.yml:
yaml
# .github/workflows/cmake.yml
name: CMake Build
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure CMake
run: cmake -B build -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --parallel
- name: Test
working-directory: build
run: ctest --output-on-failureMatrix Build — Cross-Platform
yaml
# .github/workflows/cmake-matrix.yml
name: CMake Matrix Build
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
name: ${{ matrix.os }} - ${{ matrix.compiler }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false # Không cancel jobs khác khi 1 job fail
matrix:
include:
# Ubuntu với GCC
- os: ubuntu-latest
compiler: gcc
cc: gcc-12
cxx: g++-12
# Ubuntu với Clang
- os: ubuntu-latest
compiler: clang
cc: clang-15
cxx: clang++-15
# Windows với MSVC
- os: windows-latest
compiler: msvc
cc: cl
cxx: cl
# macOS với Apple Clang
- os: macos-latest
compiler: apple-clang
cc: clang
cxx: clang++
steps:
- name: Checkout
uses: actions/checkout@v4
# Linux: Install compilers
- name: Install GCC 12 (Ubuntu)
if: matrix.os == 'ubuntu-latest' && matrix.compiler == 'gcc'
run: |
sudo apt-get update
sudo apt-get install -y gcc-12 g++-12
- name: Install Clang 15 (Ubuntu)
if: matrix.os == 'ubuntu-latest' && matrix.compiler == 'clang'
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 15
# Windows: Setup MSVC
- name: Setup MSVC (Windows)
if: matrix.os == 'windows-latest'
uses: ilammy/msvc-dev-cmd@v1
# Configure với Ninja (cross-platform)
- name: Install Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
- name: Configure CMake
env:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
run: >
cmake -B build -G Ninja
-DCMAKE_BUILD_TYPE=Release
-DBUILD_TESTS=ON
- name: Build
run: cmake --build build --parallel
- name: Test
working-directory: build
run: ctest --output-on-failure --parallel 4
# Upload artifacts
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.os }}-${{ matrix.compiler }}
path: |
build/bin/*
build/*.exeSanitizer CI Jobs
yaml
# .github/workflows/sanitizers.yml
name: Sanitizer Checks
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
# AddressSanitizer
asan:
name: AddressSanitizer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y ninja-build
- name: Configure with ASan
env:
CC: clang-15
CXX: clang++-15
run: >
cmake -B build -G Ninja
-DCMAKE_BUILD_TYPE=Debug
-DENABLE_ASAN=ON
-DBUILD_TESTS=ON
- name: Build
run: cmake --build build
- name: Run tests with ASan
env:
ASAN_OPTIONS: detect_leaks=1:abort_on_error=1
run: |
cd build && ctest --output-on-failure
# UndefinedBehaviorSanitizer
ubsan:
name: UndefinedBehaviorSanitizer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure with UBSan
env:
CC: clang-15
CXX: clang++-15
run: >
cmake -B build -G Ninja
-DCMAKE_BUILD_TYPE=Debug
-DENABLE_UBSAN=ON
-DBUILD_TESTS=ON
- name: Build
run: cmake --build build
- name: Run tests with UBSan
env:
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
run: |
cd build && ctest --output-on-failure
# ThreadSanitizer
tsan:
name: ThreadSanitizer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure with TSan
env:
CC: clang-15
CXX: clang++-15
run: >
cmake -B build -G Ninja
-DCMAKE_BUILD_TYPE=Debug
-DENABLE_TSAN=ON
-DBUILD_TESTS=ON
- name: Build
run: cmake --build build
- name: Run tests with TSan
env:
TSAN_OPTIONS: halt_on_error=1
run: |
cd build && ctest --output-on-failureStatic Analysis in CI
yaml
# .github/workflows/static-analysis.yml
name: Static Analysis
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
clang-tidy:
name: clang-tidy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install clang-tidy
run: |
sudo apt-get update
sudo apt-get install -y clang-tidy-15
- name: Configure (generate compile_commands.json)
run: |
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
- name: Run clang-tidy
run: |
find src include -name '*.cpp' -o -name '*.hpp' | \
xargs clang-tidy-15 \
-p build \
--warnings-as-errors='*' \
--header-filter='include/.*'
cppcheck:
name: cppcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install cppcheck
run: sudo apt-get install -y cppcheck
- name: Run cppcheck
run: |
cppcheck \
--enable=warning,performance,portability \
--error-exitcode=1 \
--suppress=missingIncludeSystem \
--inline-suppr \
-I include \
src/Conan Integration
yaml
# .github/workflows/cmake-conan.yml
name: CMake + Conan
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Conan
run: |
pip install conan
conan profile detect --force
- name: Install Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
- name: Install dependencies
run: |
conan install . \
--output-folder=build \
--build=missing \
-s build_type=Release
- name: Configure CMake
run: |
cmake -B build -G Ninja \
--toolchain build/conan_toolchain.cmake \
-DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build
- name: Test
run: cd build && ctestVcpkg Integration
yaml
# .github/workflows/cmake-vcpkg.yml
name: CMake + Vcpkg
on:
push:
branches: [main]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Setup vcpkg
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: 'a1234567...' # Pin version
- name: Configure CMake
run: |
cmake -B build \
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake \
-DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --config Release
- name: Test
run: ctest --test-dir build --config ReleaseCCache in CI
yaml
# .github/workflows/cmake-cached.yml
name: CMake Build (Cached)
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install ccache
run: sudo apt-get install -y ccache
# Cache ccache directory
- name: Cache ccache
uses: actions/cache@v4
with:
path: ~/.cache/ccache
key: ccache-${{ runner.os }}-${{ hashFiles('**/*.cpp', '**/*.hpp') }}
restore-keys: |
ccache-${{ runner.os }}-
- name: Configure ccache
run: |
ccache --set-config=max_size=500M
ccache --zero-stats
- name: Configure CMake
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build build
- name: Show ccache stats
run: ccache --show-stats
- name: Test
run: cd build && ctestRelease Workflow
yaml
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- 'v*' # Trigger on version tags
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
artifact_name: myapp-linux
asset_name: myapp-linux-amd64
- os: windows-latest
artifact_name: myapp-windows
asset_name: myapp-windows-amd64.exe
- os: macos-latest
artifact_name: myapp-macos
asset_name: myapp-macos-arm64
steps:
- uses: actions/checkout@v4
- name: Configure
run: |
cmake -B build \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_LTO=ON
- name: Build
run: cmake --build build --config Release
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: build/bin/
release:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Create Release
uses: softprops/action-gh-release@v1
with:
files: |
myapp-linux/*
myapp-windows/*
myapp-macos/*
generate_release_notes: trueComplete Production Pipeline
yaml
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
CMAKE_BUILD_PARALLEL_LEVEL: 4
jobs:
# ====================================
# Stage 1: Format & Lint (Fast fail)
# ====================================
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check clang-format
run: |
find src include -name '*.cpp' -o -name '*.hpp' | \
xargs clang-format --dry-run --Werror
# ====================================
# Stage 2: Build Matrix
# ====================================
build:
needs: lint
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
build_type: [Debug, Release]
steps:
- uses: actions/checkout@v4
- uses: seanmiddleditch/gha-setup-ninja@v4
- name: Configure
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build --output-on-failure
# ====================================
# Stage 3: Sanitizers (Linux only)
# ====================================
sanitizers:
needs: lint
runs-on: ubuntu-latest
strategy:
matrix:
sanitizer: [asan, ubsan, tsan]
steps:
- uses: actions/checkout@v4
- name: Build with ${{ matrix.sanitizer }}
env:
CC: clang-15
CXX: clang++-15
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DENABLE_${{ matrix.sanitizer | upper }}=ON
cmake --build build
- name: Test
run: ctest --test-dir build --output-on-failure
# ====================================
# Stage 4: Code Coverage
# ====================================
coverage:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install lcov
run: sudo apt-get install -y lcov
- name: Build with coverage
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS="--coverage"
cmake --build build
- name: Run tests
run: ctest --test-dir build
- name: Generate coverage report
run: |
lcov --capture --directory build --output-file coverage.info
lcov --remove coverage.info '/usr/*' --output-file coverage.info
- name: Upload to Codecov
uses: codecov/codecov-action@v3
with:
file: coverage.infoPro Tips
💡 CI OPTIMIZATION TIPS
- Cache aggressively: ccache, vcpkg binary cache, Conan cache
- Fail fast: Lint/format checks first (< 30 seconds)
- Parallel matrix: Run OS builds concurrently
- Self-hosted runners: Faster builds cho large projects
- Conditional jobs: Sanitizers chỉ on PRs, không on every push