Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/cache-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ All workflows that save or delete caches already declare this permission.
| `_thread_sanitizer.yml` | `build_and_test_tsan` |
| `_address_sanitizer.yml` | `build_and_test_asan_ubsan_lsan` |
| `_linter.yml` | (one per linter bazel-config) |
| `_coverage_report.yml` | `coverage_report` |
| `_coverage_report.yml` | `coverage_report` (Linux); QNX variant runs without disk cache (`disk-cache: ""`), gated on QNX secret availability like `_codeql.yml` |
| `_codeql.yml` | _(none — empty)_ |
| `build_and_test_qnx.yml` | `build_and_test_qnx` |
| `deploy_docs.yml` | `build_docs` |
Expand Down
90 changes: 87 additions & 3 deletions .github/workflows/_coverage_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,16 @@ on:
secrets:
UBUNTU_SNAPSHOT_MIRROR_URL:
required: false
SCORE_QNX_LICENSE:
required: false
SCORE_QNX_USER:
required: false
SCORE_QNX_PASSWORD:
required: false

jobs:
coverage:
name: Build & Test
coverage-linux:
name: Build & Test (Linux)
runs-on: ubuntu-24.04
permissions:
contents: read
Expand All @@ -57,13 +63,91 @@ jobs:
if: inputs.fetch-only != 'true' && steps.run-coverage.outcome == 'success'
id: generate-report
run: |
bazel run --config=ci //quality/coverage:generate_coverage_html -- \
bazel run --config=ci @score_coverage//:generate_coverage_html -- \
--yaml quality/coverage/coverage_justifications.yaml \
--testlogs-subdir score \
--archive "${{ github.event.repository.name }}_coverage_report_${{ github.sha }}" \
cpp_coverage_linux
env:
# Report-only: the effective-coverage gate is not enforced yet.
COVERAGE_THRESHOLD: "0"

- name: Upload coverage artifacts
if: inputs.fetch-only != 'true' && steps.generate-report.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: ${{ github.event.repository.name }}_coverage_report_${{ github.sha }}
path: ${{ github.event.repository.name }}_coverage_report_${{ github.sha }}.zip

# Whether to additionally collect the --config=qnx coverage is derived
# automatically from secret availability rather than a caller-supplied
# flag: it only runs when the QNX license secret has actually been
# provided (e.g. not forwarded by cache-warming callers).
determine-qnx:
name: Determine QNX availability
runs-on: ubuntu-24.04
outputs:
available: ${{ steps.qnx.outputs.available }}
steps:
- id: qnx
run: |
if [[ -n "${{ secrets.SCORE_QNX_LICENSE }}" ]]; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi

coverage-qnx:
name: Build & Test (QNX, --config=qnx)
needs: [determine-qnx]
if: inputs.fetch-only != 'true' && needs.determine-qnx.outputs.available == 'true'
runs-on: ubuntu-24.04
permissions:
contents: read
actions: write
steps:
- uses: actions/checkout@v6.0.2

- name: Setup QNX and QEMU environment
uses: ./.github/actions/00_infrastructure/setup_qnx_environment
with:
qnx-license: ${{ secrets.SCORE_QNX_LICENSE }}

- uses: ./.github/actions/00_infrastructure/prepare_bazel_environment
with:
disk-cache: ""
cache-mode: ${{ inputs.cache-mode }}
ubuntu-snapshot-mirror-url: ${{ secrets.UBUNTU_SNAPSHOT_MIRROR_URL }}

- name: Run coverage (QNX, --config=qnx)
id: run-coverage-qnx
env:
SCORE_QNX_USER: ${{ secrets.SCORE_QNX_USER }}
SCORE_QNX_PASSWORD: ${{ secrets.SCORE_QNX_PASSWORD }}
run: bazel coverage --config=ci --config=qnx //score/... --build_tests_only

- name: Generate coverage report (QNX)
if: steps.run-coverage-qnx.outcome == 'success'
id: generate-report-qnx
env:
# Report-only: the effective-coverage gate is not enforced yet.
COVERAGE_THRESHOLD: "0"
run: |
# Note: no --config=qnx here. This step only reads the coverage
# data produced by the previous "bazel coverage" step and runs a
# host-side Python tool; --config=qnx sets the *target* platform to
# QNX for the whole invocation, and no Python toolchain is
# registered for that platform, so the build fails to resolve one.
bazel run --config=ci @score_coverage//:generate_coverage_html -- \
--platform qnx \
--yaml quality/coverage/coverage_justifications.yaml \
--testlogs-subdir score \
--archive "${{ github.event.repository.name }}_coverage_report_qnx_${{ github.sha }}" \
cpp_coverage_qnx

- name: Upload coverage artifacts (QNX)
if: steps.generate-report-qnx.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: ${{ github.event.repository.name }}_coverage_report_qnx_${{ github.sha }}
path: ${{ github.event.repository.name }}_coverage_report_qnx_${{ github.sha }}.zip
39 changes: 36 additions & 3 deletions .github/workflows/nightly_quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,21 @@
# Workflow: Run nightly quality jobs and publish results to GitHub Pages.
#
# Runs every night at midnight UTC:
# - Coverage : full C++ test suite with llvm-cov → HTML report
# - Coverage : full C++ test suite with llvm-cov (Linux) and gcov (QNX, when
# QNX secrets are available) → HTML reports
# - Clang-Tidy: static analysis across all C++ targets → SARIF findings
# - Clippy : static analysis across all Rust targets → SARIF findings
# - CodeQL : MISRA C++ compliance analysis → CSV findings
#
# After all jobs complete, deploy-quality-reports:
# 1. Downloads the coverage HTML artifact, clang-tidy/clippy SARIF findings, and CodeQL CSV
# 1. Downloads the coverage HTML artifacts (Linux + QNX), clang-tidy/clippy SARIF findings, and CodeQL CSV
# 2. Runs `bazel run //quality/dashboard:generate_dashboard` to produce the KPI dashboard
# 3. Uploads everything as nightly-quality-reports artifact for docs.yml to deploy
#
# Deployed URL structure:
# https://eclipse-score.github.io/communication/latest/quality/index.html ← dashboard
# https://eclipse-score.github.io/communication/latest/quality/coverage/index.html ← lcov HTML
# https://eclipse-score.github.io/communication/latest/quality/coverage/index.html ← lcov HTML (Linux)
# https://eclipse-score.github.io/communication/latest/quality/coverage_qnx/index.html ← gcov HTML (QNX)
# https://eclipse-score.github.io/communication/latest/quality/clang-tidy.sarif ← clang-tidy SARIF
# https://eclipse-score.github.io/communication/latest/quality/clippy.sarif ← clippy SARIF
# https://eclipse-score.github.io/communication/latest/quality/codeql/index.html ← CodeQL report
Expand Down Expand Up @@ -65,6 +67,9 @@ jobs:
actions: write
secrets:
UBUNTU_SNAPSHOT_MIRROR_URL: ${{ secrets.UBUNTU_SNAPSHOT_MIRROR_URL }}
SCORE_QNX_LICENSE: ${{ secrets.SCORE_QNX_LICENSE }}
SCORE_QNX_USER: ${{ secrets.SCORE_QNX_USER }}
SCORE_QNX_PASSWORD: ${{ secrets.SCORE_QNX_PASSWORD }}

# --------------------------------------------------------------------
# Quality job 2: Linters
Expand Down Expand Up @@ -140,6 +145,33 @@ jobs:
/tmp/coverage_zip \
"${GITHUB_WORKSPACE}/_quality/coverage"

# ------------------------------------------------------------------
# Download QNX Coverage artifact. This is a best-effort step: the
# inner coverage-qnx job only runs when QNX secrets were forwarded
# to _coverage_report.yml, so the artifact may legitimately not
# exist (continue-on-error, matching the clang-tidy/clippy pattern).
# ------------------------------------------------------------------
- name: Download QNX coverage artifact
id: download-coverage-qnx
if: needs.run-coverage.result == 'success'
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: ${{ github.event.repository.name }}_coverage_report_qnx_${{ github.sha }}
path: /tmp/coverage_zip_qnx

# ------------------------------------------------------------------
# Extract QNX coverage artifact to a known location alongside the
# Linux report for docs.yml to deploy.
# ------------------------------------------------------------------
- name: Extract QNX coverage HTML report
if: steps.download-coverage-qnx.outcome == 'success'
run: |
bash quality/scripts/extract_coverage_artifact.sh \
/tmp/coverage_zip_qnx \
"${GITHUB_WORKSPACE}/_quality/coverage_qnx" \
cpp_coverage_qnx

# ------------------------------------------------------------------
# Download clang-tidy findings artifact
# ------------------------------------------------------------------
Expand Down Expand Up @@ -231,6 +263,7 @@ jobs:
# absent files gracefully (shows N/A for that metric).
bazel run --config=ci //quality/dashboard:generate_dashboard -- \
--lcov /tmp/coverage_zip/extracted/artifacts/coverage_report.dat \
--lcov-qnx /tmp/coverage_zip_qnx/extracted/artifacts/coverage_report.dat \
--clang-tidy /tmp/clang_tidy/clang-tidy.sarif \
--clippy /tmp/clippy/clippy.sarif \
--codeql "${GITHUB_WORKSPACE}/_quality/codeql_findings.sarif" \
Expand Down
3 changes: 2 additions & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ use_repo(
"score_qcc_x86_64_toolchain_pkg",
)

bazel_dep(name = "score_coverage", version = "0.3.1", dev_dependency = True)
bazel_dep(name = "score_itf", version = "0.5.0", dev_dependency = True)

# Use pinned score_itf fork commit for integration testing changes.
Expand Down Expand Up @@ -256,7 +257,7 @@ llvm.toolchain(
"@score_cpp_policies//sanitizers/features:lsan",
"@score_cpp_policies//sanitizers/features:tsan",
"//quality/compiler_warnings:treat_warnings_as_errors",
"//quality/coverage:enable_llvm_coverage_for_death_tests",
"@score_coverage//:enable_llvm_coverage_for_death_tests",
],
link_libs = {"": [
"-lrt",
Expand Down
2 changes: 2 additions & 0 deletions MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion bazel/rules/generate_quality_links.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@ def _generate_quality_links_impl(ctx):
)

release_coverage_asset_url = _release_asset_url(docs_base_url, docs_version, "coverage_report")
release_coverage_qnx_asset_url = _release_asset_url(docs_base_url, docs_version, "coverage_report_qnx")
release_clang_tidy_asset_url = _release_asset_url(docs_base_url, docs_version, "clang_tidy_report")
release_codeql_asset_url = _release_asset_url(docs_base_url, docs_version, "codeql_report")
release_dashboard_asset_url = _release_asset_url(docs_base_url, docs_version, "quality_dashboard")

if docs_version == "latest":
# quality reports are published alongside the latest/ docs
coverage_ref = "`Coverage report <quality/coverage/index.html>`__"
coverage_qnx_ref = "`Coverage report (QNX) <quality/coverage_qnx/index.html>`__"
dashboard_ref = "`Quality Dashboard <quality/index.html>`__"
clang_tidy_ref = "`Clang-Tidy report <quality/clang_tidy_findings.txt>`__"
codeql_ref = "`CodeQL findings <quality/codeql_findings.sarif>`__"
Expand All @@ -87,6 +89,14 @@ def _generate_quality_links_impl(ctx):
else:
coverage_ref = ("`Coverage report (latest) <" + latest +
"/quality/coverage/index.html>`__")
if release_coverage_qnx_asset_url:
coverage_qnx_ref = (
"`Coverage report (QNX, release artifact) <" +
release_coverage_qnx_asset_url + ">`__"
)
else:
coverage_qnx_ref = ("`Coverage report (QNX, latest) <" + latest +
"/quality/coverage_qnx/index.html>`__")
if release_dashboard_asset_url:
dashboard_ref = (
"`Quality Dashboard (release artifact) <" +
Expand Down Expand Up @@ -129,7 +139,11 @@ def _generate_quality_links_impl(ctx):
# local build — no published reports; show the equivalent bazel command
coverage_ref = (
"*local build* — run " +
"``bazel run //quality/coverage:generate_coverage_html``"
"``bazel run @score_coverage//:generate_coverage_html``"
)
coverage_qnx_ref = (
"*local build* — run " +
"``bazel run @score_coverage//:generate_coverage_html -- --platform qnx``"
)
dashboard_ref = (
"*local build* — dashboard only available on GitHub Pages"
Expand All @@ -153,6 +167,7 @@ def _generate_quality_links_impl(ctx):
content = (
":orphan:\n\n" +
".. |coverage_report_link| replace:: " + coverage_ref + "\n" +
".. |coverage_qnx_report_link| replace:: " + coverage_qnx_ref + "\n" +
".. |quality_dashboard_link| replace:: " + dashboard_ref + "\n" +
".. |clang_tidy_report_link| replace:: " + clang_tidy_ref + "\n" +
".. |codeql_report_link| replace:: " + codeql_ref + "\n" +
Expand Down
11 changes: 8 additions & 3 deletions docs/sphinx/quality_reports.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ nightly run of the `Nightly Quality Jobs`_ workflow.
- Description
- Report
* - Coverage
- Line, function, and branch coverage from C++ unit tests (gcov/lcov)
- Line, function, and branch coverage from C++ unit tests (Linux, llvm-cov/lcov)
- |coverage_report_link|
* - Coverage (QNX)
- Line, function, and branch coverage from C++ unit tests built for QNX
(``--config=qnx``, gcov/lcov). Only generated when QNX secrets are
available.
- |coverage_qnx_report_link|
* - Clang-Tidy
- Static analysis findings (errors and warnings) across all C++ targets
- |clang_tidy_report_link|
Expand All @@ -51,7 +56,7 @@ nightly run of the `Nightly Quality Jobs`_ workflow.
exclusively alongside the ``latest`` documentation on GitHub Pages.
In local Sphinx builds the link cells above show equivalent ``bazel run``
commands. In versioned release docs, every quality link (coverage,
dashboard, clang-tidy, and CodeQL) points to the corresponding report
uploaded as a release artifact for that release.
coverage (QNX), dashboard, clang-tidy, and CodeQL) points to the
corresponding report uploaded as a release artifact for that release.

.. _Nightly Quality Jobs: https://github.com/eclipse-score/communication/actions/workflows/nightly_quality.yml
Loading
Loading