Skip to content

greenc-FNAL running code coverage #2207

greenc-FNAL running code coverage

greenc-FNAL running code coverage #2207

Workflow file for this run

# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: Code Coverage
run-name: "${{ github.actor }} running code coverage"
"on":
push:
branches: [main, develop]
pull_request:
issue_comment:
types: [created]
workflow_dispatch:
inputs:
ref:
description: "The branch, ref, or SHA to checkout. Defaults to the repository's default branch."
required: false
type: string
repo:
description: "The repository to checkout. Defaults to the current repository."
required: false
type: string
pr-base-sha:
description: "The base SHA for diffing (PR base). Defaults to event-provided value."
required: false
type: string
phlex-coverage-compiler:
description: "Compiler to use for coverage build (gcc or clang)"
required: false
default: "clang"
phlex-enable-form:
description: "Enable FORM integration (set to OFF to exclude FORM sources)"
required: false
default: "ON"
permissions:
contents: read
pull-requests: read
jobs:
setup:
if: >
github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || github.event_name == 'push' ||
(
github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) &&
startsWith(github.event.comment.body, format('@{0}bot coverage', github.event.repository.name))
)
runs-on: ubuntu-latest
outputs:
is_act: ${{ steps.setup.outputs.is_act }}
ref: ${{ steps.setup.outputs.ref }}
repo: ${{ steps.setup.outputs.repo }}
base_sha: ${{ steps.setup.outputs.base_sha }}
pr_number: ${{ steps.setup.outputs.pr_number }}
checkout_path: ${{ steps.setup.outputs.checkout_path }}
build_path: ${{ steps.setup.outputs.build_path }}
has_changes: ${{ steps.setup.outputs.has_changes }}
steps:
- name: Workflow setup
id: setup
uses: Framework-R-D/phlex/.github/actions/workflow-setup@main
with:
ref: ${{ inputs.ref }}
repo: ${{ inputs.repo }}
pr-base-sha: ${{ inputs.pr-base-sha }}
file-type: |
cpp
cmake
python
coverage:
needs: setup
if: >
always() && (github.event_name == 'workflow_dispatch' || needs.setup.outputs.has_changes == 'true')
runs-on: ubuntu-24.04
container:
image: ghcr.io/framework-r-d/phlex-ci:latest
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
outputs:
has_coverage_xml: ${{ steps.coverage_outputs.outputs.has_coverage_xml }}
has_coverage_html: ${{ steps.coverage_outputs.outputs.has_coverage_html }}
has_coverage_llvm_info: ${{ steps.coverage_outputs.outputs.has_coverage_llvm_info }}
artifact_upload_available: ${{ steps.artifact_runtime.outputs.available }}
steps:
- name: Determine coverage options
id: coverage_options
shell: bash
# yamllint disable rule:line-length
env:
REQUESTED_COMPILER:
${{ github.event_name == 'workflow_dispatch' && github.event.inputs['phlex-coverage-compiler'] || '' }}
REQUESTED_FORM:
${{ github.event_name == 'workflow_dispatch' && github.event.inputs['phlex-enable-form'] || '' }}
run: |
set -euo pipefail
compiler="${REQUESTED_COMPILER:-clang}"
form="${REQUESTED_FORM:-ON}"
echo "compiler=$compiler" >> "$GITHUB_OUTPUT"
echo "enable_form=$form" >> "$GITHUB_OUTPUT"
# yamllint enable
- name: Check out source code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ needs.setup.outputs.ref }}
path: ${{ needs.setup.outputs.checkout_path }}
repository: ${{ needs.setup.outputs.repo }}
persist-credentials: false
- name: Setup build environment
uses: Framework-R-D/phlex/.github/actions/setup-build-env@main
with:
build-path: ${{ needs.setup.outputs.build_path }}
- name: Announce CMake configuration
run: echo "➡️ Configuring CMake for coverage..."
- name: Configure CMake with GCC coverage
id: configure_gcc
if: ${{ steps.coverage_options.outputs.compiler == 'gcc' }}
uses: Framework-R-D/phlex/.github/actions/configure-cmake@main
with:
cpp-compiler: g++
preset: coverage-gcc
source-path: ${{ needs.setup.outputs.checkout_path }}
build-path: ${{ needs.setup.outputs.build_path }}
enable-form: ${{ steps.coverage_options.outputs.enable_form }}
form-root-storage: ${{ steps.coverage_options.outputs.enable_form }}
- name: Configure CMake with Clang coverage
id: configure_clang
if: ${{ steps.coverage_options.outputs.compiler == 'clang' }}
uses: Framework-R-D/phlex/.github/actions/configure-cmake@main
with:
cpp-compiler: clang++
preset: coverage-clang
source-path: ${{ needs.setup.outputs.checkout_path }}
build-path: ${{ needs.setup.outputs.build_path }}
enable-form: ${{ steps.coverage_options.outputs.enable_form }}
form-root-storage: ${{ steps.coverage_options.outputs.enable_form }}
- name: Unknown Compiler Error
if:
${{ steps.coverage_options.outputs.compiler != 'gcc' && steps.coverage_options.outputs.compiler != 'clang' }}
env:
COMPILER: ${{ steps.coverage_options.outputs.compiler }}
run: |
echo "ERROR: Unknown compiler '${COMPILER}'. Must be 'gcc' or 'clang'."
exit 1
- name: Announce build
run: echo "➡️ Building with coverage instrumentation..."
- name: Build with coverage instrumentation
id: build
uses: Framework-R-D/phlex/.github/actions/build-cmake@main
with:
build-path: ${{ needs.setup.outputs.build_path }}
- name: Run tests with coverage
working-directory: ${{ needs.setup.outputs.build_path }}
run: |
. /entrypoint.sh
echo "➡️ Running tests with coverage..."
PROFILE_ROOT="$(pwd)/test/profraw"
echo "Cleaning LLVM profile directory: $PROFILE_ROOT"
rm -rf "$PROFILE_ROOT"
mkdir -p "$PROFILE_ROOT"
export LLVM_PROFILE_FILE="$PROFILE_ROOT/%m-%p.profraw"
echo "::group::Running ctest for coverage"
if ctest --progress --output-on-failure -j "$(nproc)"; then
echo "::endgroup::"
echo "✅ All tests passed."
else
echo "::endgroup::"
echo "::error::Some tests failed."
exit 1
fi
- name: Generate coverage reports (GCC)
id: report_gcc
if: ${{ steps.coverage_options.outputs.compiler == 'gcc' }}
shell: bash
working-directory: ${{ needs.setup.outputs.build_path }}
run: |
. /entrypoint.sh
echo "➡️ Generating coverage reports for GCC..."
echo "::group::Running coverage-gcov target"
if cmake --build . --target coverage-gcov -v; then
echo "::endgroup::"
echo "✅ GCC coverage report generation succeeded."
else
echo "::endgroup::"
echo "::error::GCC coverage report generation failed."
exit 1
fi
- name: Generate coverage reports (Clang)
id: report_clang
if: ${{ steps.coverage_options.outputs.compiler == 'clang' }}
shell: bash
working-directory: ${{ needs.setup.outputs.build_path }}
run: |
. /entrypoint.sh
echo "➡️ Generating coverage reports for Clang..."
echo "::group::Running coverage-llvm target"
if cmake --build . --target coverage-llvm -v; then
echo "::endgroup::"
echo "✅ Clang coverage report generation succeeded."
else
echo "::endgroup::"
echo "::error::Clang coverage report generation failed."
exit 1
fi
- name: Generate Python coverage report
id: report_python
if: ${{ steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped' }}
shell: bash
working-directory: ${{ needs.setup.outputs.build_path }}
run: |
. /entrypoint.sh
echo "➡️ Generating Python coverage report..."
echo "::group::Running coverage-python target"
# Check if pytest-cov is available
if python3 -c "import pytest_cov" 2>/dev/null; then
if cmake --build . --target coverage-python -v; then
echo "::endgroup::"
echo "✅ Python coverage report generation succeeded."
else
echo "::endgroup::"
echo "::warning::Python coverage report generation failed (non-fatal)."
fi
else
echo "::endgroup::"
echo "::notice::pytest-cov not available; skipping Python coverage report."
fi
- name: Capture coverage artifact availability
id: coverage_outputs
if: ${{ steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped' }}
shell: bash
working-directory: ${{ needs.setup.outputs.build_path }}
run: |
if [ -f coverage.xml ]; then
echo "has_coverage_xml=true" >> "$GITHUB_OUTPUT"
else
echo "has_coverage_xml=false" >> "$GITHUB_OUTPUT"
fi
if [ -d coverage-html ]; then
echo "has_coverage_html=true" >> "$GITHUB_OUTPUT"
else
echo "has_coverage_html=false" >> "$GITHUB_OUTPUT"
fi
if [ -f coverage-llvm.info ]; then
echo "has_coverage_llvm_info=true" >> "$GITHUB_OUTPUT"
else
echo "has_coverage_llvm_info=false" >> "$GITHUB_OUTPUT"
fi
if [ -f coverage-python.xml ]; then
echo "has_coverage_python_xml=true" >> "$GITHUB_OUTPUT"
else
echo "has_coverage_python_xml=false" >> "$GITHUB_OUTPUT"
fi
- name: Unknown Coverage Report Error
if: ${{ steps.report_gcc.outcome == 'skipped' && steps.report_clang.outcome == 'skipped' }}
run: |
echo "ERROR: No coverage report was generated. Must run either GCC or Clang coverage report step."
exit 1
- name: Prepare coverage artifact bundle
if: ${{ steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped' }}
shell: bash
working-directory: ${{ needs.setup.outputs.build_path }}
run: |
set -euo pipefail
ARTIFACT_DIR="$GITHUB_WORKSPACE/coverage-artifacts"
rm -rf "$ARTIFACT_DIR"
mkdir -p "$ARTIFACT_DIR"
for file in \
coverage-llvm.txt \
coverage-llvm.info \
coverage.profdata \
coverage.xml \
coverage.info \
coverage.info.final \
coverage-python.xml; do
if [ -f "$file" ]; then
cp "$file" "$ARTIFACT_DIR/"
fi
done
if [ -d coverage-html ]; then cp -R coverage-html "$ARTIFACT_DIR/"; fi
if [ -d coverage-python-html ]; then cp -R coverage-python-html "$ARTIFACT_DIR/"; fi
- name: Detect artifact upload availability
id: artifact_runtime
if: ${{ steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped' }}
shell: bash
# yamllint disable rule:line-length
run: |
if [ "${GITHUB_ACTOR}" = "nektos/act" ] || [ "${ACT:-}" = "true" ]; then
echo "::notice::Artifact upload disabled for local act executions."
echo "available=false" >> "$GITHUB_OUTPUT"
elif [ -z "${ACTIONS_RUNTIME_TOKEN:-}" ]; then
echo "::warning::ACTIONS_RUNTIME_TOKEN missing; continuing and trusting actions/upload-artifact to handle authentication."
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=true" >> "$GITHUB_OUTPUT"
fi
# yamllint enable
- name: Install Node.js runtime for artifact publishing
if:
${{ (steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped') &&
steps.artifact_runtime.outputs.available == 'true' && github.actor != 'nektos/act' }}
run: |
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get update
apt-get install -y nodejs
- name: Upload coverage bundle
if:
${{ (steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped') &&
steps.artifact_runtime.outputs.available == 'true' && github.actor != 'nektos/act' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: coverage-generated
path: coverage-artifacts/
retention-days: 30
coverage-upload:
needs: coverage
if:
${{ needs.coverage.result == 'success' && needs.coverage.outputs.artifact_upload_available == 'true' &&
github.actor != 'nektos/act' }}
runs-on: ubuntu-latest
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
steps:
- name: Check out source code for Codecov mapping
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Download coverage bundle
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: coverage-generated
path: coverage-artifacts
- name: Determine coverage files for Codecov
id: codecov_targets
run: |
set -euo pipefail
files=()
if [ -f coverage-artifacts/coverage.xml ]; then
files+=("coverage-artifacts/coverage.xml")
fi
if [ -f coverage-artifacts/coverage-llvm.info ]; then
files+=("coverage-artifacts/coverage-llvm.info")
fi
if [ -f coverage-artifacts/coverage-python.xml ]; then
files+=("coverage-artifacts/coverage-python.xml")
fi
ls -al coverage-artifacts || true
if [ "${#files[@]}" -eq 0 ]; then
echo "No coverage files detected; skipping Codecov upload." >&2
echo "found=false" >> "$GITHUB_OUTPUT"
echo "files=" >> "$GITHUB_OUTPUT"
else
file_csv=$(IFS=,; printf '%s' "${files[*]}")
echo "Codecov upload targets: ${files[*]}"
echo "found=true" >> "$GITHUB_OUTPUT"
echo "files=${file_csv}" >> "$GITHUB_OUTPUT"
fi
- name: Upload coverage to Codecov
if: ${{ steps.codecov_targets.outputs.found == 'true' }}
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
with:
files: ${{ steps.codecov_targets.outputs.files }}
flags: unittests
name: phlex-coverage
fail_ci_if_error: true
verbose: true
root_dir: .
token: ${{ env.CODECOV_TOKEN }}
- name: Publish HTML coverage report
if: ${{ needs.coverage.outputs.has_coverage_html == 'true' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: coverage-html-report
path: coverage-artifacts/coverage-html/
if-no-files-found: warn
retention-days: 30