|
| 1 | +# |
| 2 | +# Copyright (c) 2026 Sam Darwin |
| 3 | +# Copyright (c) 2026 Alexander Grund |
| 4 | +# |
| 5 | +# Distributed under the Boost Software License, Version 1.0. (See accompanying |
| 6 | +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| 7 | +# |
| 8 | +# Official repository: https://github.com/cppalliance/burl/ |
| 9 | +# |
| 10 | + |
| 11 | +# Instructions |
| 12 | +# |
| 13 | +# After running this workflow successfully, go to https://github.com/cppalliance/burl/settings/pages |
| 14 | +# and enable github pages on the code-coverage branch. |
| 15 | +# The coverage will be hosted at https://cppalliance.github.io/burl |
| 16 | +# |
| 17 | + |
| 18 | +name: Code Coverage |
| 19 | + |
| 20 | +on: |
| 21 | + push: |
| 22 | + branches: |
| 23 | + - master |
| 24 | + - develop |
| 25 | + paths: |
| 26 | + - 'src/**' |
| 27 | + - 'include/**' |
| 28 | + - 'test/**' |
| 29 | + - '.github/workflows/code-coverage.yml' |
| 30 | + workflow_dispatch: |
| 31 | + |
| 32 | +concurrency: |
| 33 | + group: code-coverage-pages |
| 34 | + cancel-in-progress: false |
| 35 | + |
| 36 | +env: |
| 37 | + GIT_FETCH_JOBS: 8 |
| 38 | + NET_RETRY_COUNT: 5 |
| 39 | + GCOVR_COMMIT_MSG: "Update coverage data" |
| 40 | + |
| 41 | +jobs: |
| 42 | + build-linux: |
| 43 | + defaults: |
| 44 | + run: |
| 45 | + shell: bash |
| 46 | + name: Coverage (Linux) |
| 47 | + runs-on: ubuntu-24.04 |
| 48 | + timeout-minutes: 120 |
| 49 | + |
| 50 | + steps: |
| 51 | + - name: Clone Boost.Burl |
| 52 | + uses: actions/checkout@v6 |
| 53 | + with: |
| 54 | + path: burl-root |
| 55 | + |
| 56 | + - name: Setup C++ |
| 57 | + uses: alandefreitas/cpp-actions/setup-cpp@v1.9.4 |
| 58 | + id: setup-cpp |
| 59 | + with: |
| 60 | + compiler: gcc |
| 61 | + version: 13 |
| 62 | + check-latest: true |
| 63 | + |
| 64 | + - name: Install packages |
| 65 | + uses: alandefreitas/cpp-actions/package-install@v1.9.4 |
| 66 | + with: |
| 67 | + apt-get: >- |
| 68 | + build-essential libssl-dev zlib1g-dev libbrotli-dev |
| 69 | + lcov |
| 70 | +
|
| 71 | + - name: Clone Boost |
| 72 | + uses: alandefreitas/cpp-actions/boost-clone@v1.9.4 |
| 73 | + id: boost-clone |
| 74 | + with: |
| 75 | + branch: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }} |
| 76 | + boost-dir: boost-source |
| 77 | + scan-modules-dir: burl-root |
| 78 | + patches: > |
| 79 | + https://github.com/cppalliance/capy |
| 80 | + https://github.com/cppalliance/corosio |
| 81 | + https://github.com/cppalliance/http |
| 82 | +
|
| 83 | + - name: ASLR Fix |
| 84 | + run: sysctl vm.mmap_rnd_bits=28 |
| 85 | + |
| 86 | + - name: Patch Boost |
| 87 | + id: patch |
| 88 | + run: | |
| 89 | + set -xe |
| 90 | +
|
| 91 | + # Identify boost module being tested |
| 92 | + module=${GITHUB_REPOSITORY#*/} |
| 93 | + echo "module=$module" >> $GITHUB_OUTPUT |
| 94 | +
|
| 95 | + # Identify GitHub workspace root |
| 96 | + workspace_root=$(echo "$GITHUB_WORKSPACE" | sed 's/\\/\//g') |
| 97 | + echo -E "workspace_root=$workspace_root" >> $GITHUB_OUTPUT |
| 98 | +
|
| 99 | + # Remove module from boost-source |
| 100 | + rm -r "boost-source/libs/$module" || true |
| 101 | +
|
| 102 | + # Copy cached boost-source to an isolated boost-root |
| 103 | + cp -r boost-source boost-root |
| 104 | +
|
| 105 | + # Set boost-root output |
| 106 | + cd boost-root |
| 107 | + boost_root="$(pwd)" |
| 108 | + boost_root=$(echo "$boost_root" | sed 's/\\/\//g') |
| 109 | + echo -E "boost_root=$boost_root" >> $GITHUB_OUTPUT |
| 110 | + cd .. |
| 111 | +
|
| 112 | + # Patch boost-root with workspace module |
| 113 | + cp -r "$workspace_root"/burl-root "boost-root/libs/$module" |
| 114 | +
|
| 115 | + - name: Build with coverage |
| 116 | + uses: alandefreitas/cpp-actions/cmake-workflow@v1.9.4 |
| 117 | + with: |
| 118 | + source-dir: boost-root |
| 119 | + build-dir: __build_cmake_test__ |
| 120 | + build-type: Debug |
| 121 | + build-target: tests |
| 122 | + run-tests: true |
| 123 | + install-prefix: .local |
| 124 | + cxxstd: '20' |
| 125 | + cc: ${{ steps.setup-cpp.outputs.cc || 'gcc-13' }} |
| 126 | + cxx: ${{ steps.setup-cpp.outputs.cxx || 'g++-13' }} |
| 127 | + cxxflags: '--coverage -fprofile-arcs -ftest-coverage' |
| 128 | + ccflags: '--coverage -fprofile-arcs -ftest-coverage' |
| 129 | + shared: false |
| 130 | + cmake-version: '>=3.20' |
| 131 | + extra-args: | |
| 132 | + -D Boost_VERBOSE=ON |
| 133 | + -D BOOST_INCLUDE_LIBRARIES="${{ steps.patch.outputs.module }}" |
| 134 | + -D CMAKE_EXPORT_COMPILE_COMMANDS=ON |
| 135 | + package: false |
| 136 | + package-artifact: false |
| 137 | + ref-source-dir: boost-root/libs/burl |
| 138 | + |
| 139 | + - name: Install Python |
| 140 | + uses: actions/setup-python@v6 |
| 141 | + with: |
| 142 | + python-version: '3.13' |
| 143 | + |
| 144 | + - name: Install Python packages |
| 145 | + run: pip install gcovr |
| 146 | + |
| 147 | + - name: Checkout ci-automation |
| 148 | + uses: actions/checkout@v6 |
| 149 | + with: |
| 150 | + repository: cppalliance/ci-automation |
| 151 | + path: ci-automation |
| 152 | + |
| 153 | + - name: Generate gcovr report |
| 154 | + run: | |
| 155 | + set -xe |
| 156 | + module=$(basename ${GITHUB_REPOSITORY}) |
| 157 | + gcov_tool="gcov-${{ steps.setup-cpp.outputs.version-major }}" |
| 158 | + command -v "$gcov_tool" >/dev/null 2>&1 || gcov_tool="gcov" |
| 159 | + mkdir -p gcovr |
| 160 | +
|
| 161 | + # First pass: collect raw coverage data into JSON |
| 162 | + gcovr \ |
| 163 | + --root boost-root \ |
| 164 | + --gcov-executable "$gcov_tool" \ |
| 165 | + --merge-mode-functions separate \ |
| 166 | + --sort uncovered-percent \ |
| 167 | + --html-title "${module}" \ |
| 168 | + --merge-lines \ |
| 169 | + --exclude-unreachable-branches \ |
| 170 | + --exclude-throw-branches \ |
| 171 | + --exclude '.*/example/.*' \ |
| 172 | + --exclude '.*/examples/.*' \ |
| 173 | + --filter ".*/${module}/.*" \ |
| 174 | + --html --output gcovr/index.html \ |
| 175 | + --json-summary-pretty --json-summary gcovr/summary.json \ |
| 176 | + --json gcovr/coverage-raw.json \ |
| 177 | + boost-root/__build_cmake_test__ |
| 178 | +
|
| 179 | + # Fix paths for repo-relative display |
| 180 | + python3 ci-automation/scripts/fix_paths.py \ |
| 181 | + gcovr/coverage-raw.json \ |
| 182 | + gcovr/coverage-fixed.json \ |
| 183 | + --repo "${module}" |
| 184 | +
|
| 185 | + # Create symlinks so gcovr can find source files at repo-relative paths |
| 186 | + ln -sfn "boost-root/libs/${module}/include" include 2>/dev/null || true |
| 187 | + ln -sfn "boost-root/libs/${module}/src" src 2>/dev/null || true |
| 188 | +
|
| 189 | + # Second pass: generate nested HTML from fixed JSON with custom templates |
| 190 | + gcovr \ |
| 191 | + -a gcovr/coverage-fixed.json \ |
| 192 | + --merge-mode-functions separate \ |
| 193 | + --sort uncovered-percent \ |
| 194 | + --html-nested \ |
| 195 | + --html-template-dir=ci-automation/gcovr-templates/html \ |
| 196 | + --html-title "${module}" \ |
| 197 | + --merge-lines \ |
| 198 | + --exclude-unreachable-branches \ |
| 199 | + --exclude-throw-branches \ |
| 200 | + --exclude '(^|.*/)test/.*' \ |
| 201 | + --exclude '.*/example/.*' \ |
| 202 | + --exclude '.*/examples/.*' \ |
| 203 | + --html --output gcovr/index.html \ |
| 204 | + --json-summary-pretty --json-summary gcovr/summary.json |
| 205 | +
|
| 206 | + - name: Generate sidebar navigation |
| 207 | + run: python3 ci-automation/scripts/gcovr_build_tree.py gcovr |
| 208 | + |
| 209 | + - name: Generate badges |
| 210 | + run: python3 ci-automation/scripts/generate_badges.py gcovr --json gcovr/summary.json |
| 211 | + |
| 212 | + - name: Upload coverage artifact |
| 213 | + uses: actions/upload-artifact@v4 |
| 214 | + with: |
| 215 | + name: coverage-linux |
| 216 | + path: gcovr/ |
| 217 | + |
| 218 | + deploy: |
| 219 | + needs: [build-linux] |
| 220 | + if: ${{ !cancelled() }} |
| 221 | + defaults: |
| 222 | + run: |
| 223 | + shell: bash |
| 224 | + name: Deploy Coverage |
| 225 | + runs-on: ubuntu-24.04 |
| 226 | + timeout-minutes: 30 |
| 227 | + |
| 228 | + steps: |
| 229 | + - name: Checkout code |
| 230 | + uses: actions/checkout@v6 |
| 231 | + |
| 232 | + - name: Check for code-coverage Branch |
| 233 | + run: | |
| 234 | + set -xe |
| 235 | + git config --global user.name cppalliance-bot |
| 236 | + git config --global user.email cppalliance-bot@example.com |
| 237 | + git fetch origin |
| 238 | + if git branch -r | grep origin/code-coverage; then |
| 239 | + echo "The code-coverage branch exists. Continuing." |
| 240 | + else |
| 241 | + echo "The code-coverage branch does not exist. Creating it." |
| 242 | + git switch --orphan code-coverage |
| 243 | + git commit --allow-empty -m "$GCOVR_COMMIT_MSG" |
| 244 | + git push origin code-coverage |
| 245 | + git checkout $GITHUB_REF_NAME |
| 246 | + fi |
| 247 | +
|
| 248 | + - name: Checkout GitHub pages branch |
| 249 | + uses: actions/checkout@v6 |
| 250 | + with: |
| 251 | + ref: code-coverage |
| 252 | + path: gh_pages_dir |
| 253 | + |
| 254 | + - name: Download coverage artifacts |
| 255 | + uses: actions/download-artifact@v4 |
| 256 | + with: |
| 257 | + pattern: coverage-* |
| 258 | + path: coverage-artifacts/ |
| 259 | + |
| 260 | + - name: Copy coverage results |
| 261 | + run: | |
| 262 | + set -xe |
| 263 | + touch gh_pages_dir/.nojekyll |
| 264 | +
|
| 265 | + mkdir -p "gh_pages_dir/develop" |
| 266 | + mkdir -p "gh_pages_dir/master" |
| 267 | +
|
| 268 | + # Remove old single-directory layout (migration from gcovr/ to gcovr-linux/) |
| 269 | + rm -rf "gh_pages_dir/${GITHUB_REF_NAME}/gcovr" |
| 270 | +
|
| 271 | + # Copy each platform's results (only if artifact exists) |
| 272 | + for platform in linux; do |
| 273 | + if [ -d "coverage-artifacts/coverage-${platform}" ]; then |
| 274 | + rm -rf "gh_pages_dir/${GITHUB_REF_NAME}/gcovr-${platform}" |
| 275 | + cp -rp "coverage-artifacts/coverage-${platform}" \ |
| 276 | + "gh_pages_dir/${GITHUB_REF_NAME}/gcovr-${platform}" |
| 277 | + fi |
| 278 | + done |
| 279 | +
|
| 280 | + # Generate branch index pages |
| 281 | + for branch in develop master; do |
| 282 | + cat > "gh_pages_dir/${branch}/index.html" << 'HTMLEOF' |
| 283 | + <html> |
| 284 | + <head><title>Code Coverage</title></head> |
| 285 | + <body> |
| 286 | + <h2>Code Coverage Reports</h2> |
| 287 | + <ul> |
| 288 | + <li><a href="gcovr-linux/index.html">Linux (GCC)</a></li> |
| 289 | + </ul> |
| 290 | + </body> |
| 291 | + </html> |
| 292 | + HTMLEOF |
| 293 | + done |
| 294 | +
|
| 295 | + # Root index |
| 296 | + cat > gh_pages_dir/index.html << 'HTMLEOF' |
| 297 | + <html> |
| 298 | + <head></head> |
| 299 | + <body> |
| 300 | + <a href="develop/index.html">develop</a><br> |
| 301 | + <a href="master/index.html">master</a><br> |
| 302 | + </body> |
| 303 | + </html> |
| 304 | + HTMLEOF |
| 305 | +
|
| 306 | + cd gh_pages_dir |
| 307 | + git config --global user.name cppalliance-bot |
| 308 | + git config --global user.email cppalliance-bot@example.com |
| 309 | + git add . |
| 310 | + git commit --amend -m "$GCOVR_COMMIT_MSG" |
| 311 | + git push -f origin code-coverage |
0 commit comments