diff --git a/.github/actions/build-cmake/action.yaml b/.github/actions/build-cmake/action.yaml index b403b2456..bccaa938a 100644 --- a/.github/actions/build-cmake/action.yaml +++ b/.github/actions/build-cmake/action.yaml @@ -1,40 +1,40 @@ -name: 'Build with CMake' -description: 'Builds the project using CMake' +name: "Build with CMake" +description: "Builds the project using CMake" inputs: source-path: - description: 'Path where source code is checked out' + description: "Path where source code is checked out" required: false - default: 'phlex-src' + default: "phlex-src" build-path: - description: 'Path for build directory' + description: "Path for build directory" required: false - default: 'phlex-build' + default: "phlex-build" target: - description: 'CMake target to build (empty for default target)' + description: "CMake target to build (empty for default target)" required: false - default: '' + default: "" parallel-jobs: - description: 'Number of parallel jobs (empty for auto-detect)' + description: "Number of parallel jobs (empty for auto-detect)" required: false - default: '' + default: "" runs: - using: 'composite' + using: "composite" steps: - shell: bash run: | # Source the container entrypoint script . /entrypoint.sh cd "$GITHUB_WORKSPACE/$BUILD_PATH" - + # Determine parallel jobs if [ -n "$PARALLEL_JOBS" ]; then jobs_arg="-j $PARALLEL_JOBS" else jobs_arg="-j $(nproc)" fi - + # Determine target if [ -n "$TARGET" ]; then target_arg="--target $TARGET" @@ -43,7 +43,7 @@ runs: target_arg="" echo "Building default target" fi - + # Build cmake --build . $jobs_arg $target_arg env: diff --git a/.github/actions/configure-cmake/action.yaml b/.github/actions/configure-cmake/action.yaml index 7f23fd5c1..c0a2e7171 100644 --- a/.github/actions/configure-cmake/action.yaml +++ b/.github/actions/configure-cmake/action.yaml @@ -1,46 +1,46 @@ -name: 'Configure CMake' -description: 'Configures CMake with preset detection and customizable options' +name: "Configure CMake" +description: "Configures CMake with preset detection and customizable options" inputs: preset: - description: 'CMake preset to use (e.g., coverage, default)' + description: "CMake preset to use (e.g., coverage, default)" required: false - default: 'default' + default: "default" source-path: - description: 'Path where source code is checked out' + description: "Path where source code is checked out" required: false - default: 'phlex-src' + default: "phlex-src" build-path: - description: 'Path for build directory' + description: "Path for build directory" required: false - default: 'phlex-build' + default: "phlex-build" build-type: - description: 'CMake build type (Release, Debug, etc.) [do not use with coverage presets]' + description: "CMake build type (Release, Debug, etc.) [do not use with coverage presets]" required: false - default: '' + default: "" extra-options: - description: 'Additional CMake configuration options' + description: "Additional CMake configuration options" required: false - default: '' + default: "" enable-form: - description: 'Enable FORM support' + description: "Enable FORM support" required: false - default: 'ON' + default: "ON" form-root-storage: - description: 'Enable FORM root storage' + description: "Enable FORM root storage" required: false - default: 'ON' + default: "ON" generator: - description: 'Specify CMake generator' + description: "Specify CMake generator" required: false - default: 'Ninja' + default: "Ninja" cpp-compiler: - description: 'The C++ compiler to use' + description: "The C++ compiler to use" required: false - default: 'g++' + default: "g++" runs: - using: 'composite' + using: "composite" steps: - shell: bash run: | diff --git a/.github/actions/detect-act-env/action.yaml b/.github/actions/detect-act-env/action.yaml index 66e6fff46..968d7e863 100644 --- a/.github/actions/detect-act-env/action.yaml +++ b/.github/actions/detect-act-env/action.yaml @@ -1,5 +1,5 @@ name: Detect act environment -description: 'Detects if the workflow is running in the act environment' +description: "Detects if the workflow is running in the act environment" outputs: is_act: description: "True if running in act, false otherwise" diff --git a/.github/actions/detect-relevant-changes/action.yaml b/.github/actions/detect-relevant-changes/action.yaml index 3c2bdd8bb..3a5af5016 100644 --- a/.github/actions/detect-relevant-changes/action.yaml +++ b/.github/actions/detect-relevant-changes/action.yaml @@ -58,7 +58,7 @@ runs: # Types that do not support .in variants for relevance detection declare -A NO_IN_VARIANT_TYPES NO_IN_VARIANT_TYPES[jsonnet]="1" - + parse_list() { local input="$1" printf '%s' "$input" | tr ',' '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e '/^$/d' diff --git a/.github/actions/generate-build-matrix/action.yaml b/.github/actions/generate-build-matrix/action.yaml index 384d26821..39443064b 100644 --- a/.github/actions/generate-build-matrix/action.yaml +++ b/.github/actions/generate-build-matrix/action.yaml @@ -1,18 +1,18 @@ -name: 'Generate Build Matrix' -description: 'Generates a dynamic build matrix for the cmake-build workflow' +name: "Generate Build Matrix" +description: "Generates a dynamic build matrix for the cmake-build workflow" inputs: user-input: - description: 'The user-provided build combinations from a workflow_dispatch event' + description: "The user-provided build combinations from a workflow_dispatch event" required: false comment-body: - description: 'The body of the issue comment that triggered the workflow' + description: "The body of the issue comment that triggered the workflow" required: false outputs: matrix: - description: 'The generated build matrix in JSON format' + description: "The generated build matrix in JSON format" value: ${{ steps.generate.outputs.matrix }} runs: - using: 'composite' + using: "composite" steps: - id: generate run: python "$ACTION_PATH/generate_matrix.py" diff --git a/.github/actions/get-pr-info/action.yaml b/.github/actions/get-pr-info/action.yaml index e03d52ac6..d1788af6e 100644 --- a/.github/actions/get-pr-info/action.yaml +++ b/.github/actions/get-pr-info/action.yaml @@ -1,17 +1,17 @@ -name: 'Get PR Info' -description: 'Gets PR information for workflows triggered by an issue comment' +name: "Get PR Info" +description: "Gets PR information for workflows triggered by an issue comment" outputs: ref: - description: 'The head ref of the PR' + description: "The head ref of the PR" value: ${{ steps.get_pr_info.outputs.ref }} sha: - description: 'The head SHA of the PR' + description: "The head SHA of the PR" value: ${{ steps.get_pr_info.outputs.sha }} repo: - description: 'The full name of the head repository' + description: "The full name of the head repository" value: ${{ steps.get_pr_info.outputs.repo }} base_sha: - description: 'The SHA of the base branch' + description: "The SHA of the base branch" value: ${{ steps.get_pr_info.outputs.base_sha }} runs: using: "composite" diff --git a/.github/actions/handle-fix-commit/action.yaml b/.github/actions/handle-fix-commit/action.yaml index eb1d81af4..3d40a4da5 100644 --- a/.github/actions/handle-fix-commit/action.yaml +++ b/.github/actions/handle-fix-commit/action.yaml @@ -1,44 +1,44 @@ -name: 'Handle Fix Commit' -description: 'Commits changes if possible, otherwise creates a patch and comments on the PR.' +name: "Handle Fix Commit" +description: "Commits changes if possible, otherwise creates a patch and comments on the PR." outputs: changes: - description: 'Whether changes were detected' + description: "Whether changes were detected" value: ${{ steps.check_changes.outputs.changes }} pushed: - description: 'Whether changes were pushed' + description: "Whether changes were pushed" value: ${{ steps.commit_and_push.outputs.pushed }} commit_sha: - description: 'The full SHA of the pushed commit' + description: "The full SHA of the pushed commit" value: ${{ steps.commit_and_push.outputs.commit_sha }} commit_sha_short: - description: 'The short SHA of the pushed commit' + description: "The short SHA of the pushed commit" value: ${{ steps.commit_and_push.outputs.commit_sha_short }} patch_name: - description: 'The name of the patch file if created' + description: "The name of the patch file if created" value: ${{ steps.create_patch.outputs.patch_name }} inputs: tool: - description: 'The tool name reported in commit messages and PR comments.' + description: "The tool name reported in commit messages and PR comments." required: true working-directory: - description: 'The working directory for git operations.' + description: "The working directory for git operations." required: false - default: 'phlex-src' + default: "phlex-src" token: - description: 'The PAT to use for committing.' + description: "The PAT to use for committing." required: true pr-info-ref: - description: 'The ref (branch name) of the PR' + description: "The ref (branch name) of the PR" required: true pr-info-repo: - description: 'The repository of the PR' + description: "The repository of the PR" required: true retry-attempts: - description: 'The number of times to retry pushing the commit.' + description: "The number of times to retry pushing the commit." required: false - default: '6' + default: "6" runs: using: "composite" @@ -58,7 +58,7 @@ runs: if: steps.check_changes.outputs.changes == 'false' uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 with: - message: 'No automatic ${{ inputs.tool }} fixes were necessary.' + message: "No automatic ${{ inputs.tool }} fixes were necessary." - name: Get PR maintainer_can_modify property id: pr-properties diff --git a/.github/actions/setup-build-env/action.yaml b/.github/actions/setup-build-env/action.yaml index e53b9c0d4..95a870a47 100644 --- a/.github/actions/setup-build-env/action.yaml +++ b/.github/actions/setup-build-env/action.yaml @@ -1,37 +1,37 @@ -name: 'Setup Build Environment' -description: 'Sets up the Phlex build environment with source and build directories' +name: "Setup Build Environment" +description: "Sets up the Phlex build environment with source and build directories" inputs: source-path: - description: 'Path where source code is checked out' + description: "Path where source code is checked out" required: false - default: 'phlex-src' + default: "phlex-src" build-path: - description: 'Path for build directory' + description: "Path for build directory" required: false - default: 'phlex-build' + default: "phlex-build" outputs: source-dir: - description: 'Absolute path to source directory' + description: "Absolute path to source directory" value: ${{ steps.setup.outputs.source-dir }} build-dir: - description: 'Absolute path to build directory' + description: "Absolute path to build directory" value: ${{ steps.setup.outputs.build-dir }} runs: - using: 'composite' + using: "composite" steps: - id: setup shell: bash run: | # Create and export build directory mkdir -p "$GITHUB_WORKSPACE/$BUILD_PATH" - + # Set outputs printf 'source-dir=%s/%s\n' "$GITHUB_WORKSPACE" "$SOURCE_PATH" >> "$GITHUB_OUTPUT" printf 'build-dir=%s/%s\n' "$GITHUB_WORKSPACE" "$BUILD_PATH" >> "$GITHUB_OUTPUT" - + echo "Source directory: $GITHUB_WORKSPACE/$SOURCE_PATH" echo "Build directory: $GITHUB_WORKSPACE/$BUILD_PATH" env: diff --git a/.github/workflows/actionlint-check.yaml b/.github/workflows/actionlint-check.yaml index 31c68dbb3..26277ecd2 100644 --- a/.github/workflows/actionlint-check.yaml +++ b/.github/workflows/actionlint-check.yaml @@ -73,36 +73,36 @@ jobs: outputs: has_changes: ${{ steps.filter.outputs.matched }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + path: ${{ env.local_checkout_path }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} - include-globs: | - .github/workflows/**/*.yml - .github/workflows/**/*.yaml - .github/actions/**/*.yml - .github/actions/**/*.yaml + - name: Detect relevant changes + id: filter + uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + with: + repo-path: ${{ env.local_checkout_path }} + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} + include-globs: | + .github/workflows/**/*.yml + .github/workflows/**/*.yaml + .github/actions/**/*.yml + .github/actions/**/*.yaml - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No actionlint related changes detected; job will be skipped." - else - echo "::group::Actionlint relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + - name: Report detection outcome + run: | + if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then + echo "::notice::No actionlint related changes detected; job will be skipped." + else + echo "::group::Actionlint relevant files" + printf '%s\n' "${{ steps.filter.outputs.matched_files }}" + echo "::endgroup::" + fi actionlint-check: needs: [pre-check, detect-changes] @@ -120,35 +120,35 @@ jobs: contents: read steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ needs.pre-check.outputs.ref }} - path: ${{ env.local_checkout_path }} - repository: ${{ needs.pre-check.outputs.repo }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.pre-check.outputs.ref }} + path: ${{ env.local_checkout_path }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Announce actionlint check - run: echo "➡️ Running actionlint check..." + - name: Announce actionlint check + run: echo "➡️ Running actionlint check..." - - name: Run actionlint - id: lint - run: | - docker run --rm \ - -v "${{ github.workspace }}/${{ env.local_checkout_path }}:/work" \ - -w /work \ - rhysd/actionlint:latest \ - -config-file .github/actionlint.yaml - continue-on-error: true + - name: Run actionlint + id: lint + run: | + docker run --rm \ + -v "${{ github.workspace }}/${{ env.local_checkout_path }}:/work" \ + -w /work \ + rhysd/actionlint:latest \ + -config-file .github/actionlint.yaml + continue-on-error: true - - name: Evaluate actionlint result - if: always() && steps.lint.outcome != 'skipped' - run: | - if [ "${{ steps.lint.outcome }}" = 'success' ]; then - echo "✅ actionlint check passed." - else - echo "::error::actionlint check failed. Please review the output above for details." - exit 1 - fi + - name: Evaluate actionlint result + if: always() && steps.lint.outcome != 'skipped' + run: | + if [ "${{ steps.lint.outcome }}" = 'success' ]; then + echo "✅ actionlint check passed." + else + echo "::error::actionlint check failed. Please review the output above for details." + exit 1 + fi actionlint-check-skipped: needs: [pre-check, detect-changes] @@ -166,5 +166,5 @@ jobs: contents: read steps: - - name: No relevant workflow changes detected - run: echo "::notice::No actionlint relevant changes detected; check skipped." + - name: No relevant workflow changes detected + run: echo "::notice::No actionlint relevant changes detected; check skipped." diff --git a/.github/workflows/clang-format-check.yaml b/.github/workflows/clang-format-check.yaml index 43fbe14d8..8e5c6df42 100644 --- a/.github/workflows/clang-format-check.yaml +++ b/.github/workflows/clang-format-check.yaml @@ -39,32 +39,32 @@ jobs: outputs: has_changes: ${{ steps.filter.outputs.matched }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: phlex-src - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ needs.pre-check.outputs.ref }} - file-type: cpp + - name: Detect relevant changes + id: filter + uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + with: + repo-path: phlex-src + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ needs.pre-check.outputs.ref }} + file-type: cpp - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No clang-format related changes detected; job will be skipped." - else - echo "::group::Clang-format relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + - name: Report detection outcome + run: | + if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then + echo "::notice::No clang-format related changes detected; job will be skipped." + else + echo "::group::Clang-format relevant files" + printf '%s\n' "${{ steps.filter.outputs.matched_files }}" + echo "::endgroup::" + fi clang-format-check: needs: [pre-check, detect-changes] @@ -82,35 +82,35 @@ jobs: contents: read steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ needs.pre-check.outputs.ref }} - path: phlex-src - repository: ${{ needs.pre-check.outputs.repo }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.pre-check.outputs.ref }} + path: phlex-src + repository: ${{ needs.pre-check.outputs.repo }} - - name: Announce clang-format check - run: echo "➡️ Running clang-format check..." + - name: Announce clang-format check + run: echo "➡️ Running clang-format check..." - - name: Run clang-format lint - id: lint - uses: DoozyX/clang-format-lint-action@bcb4eb2cb0d707ee4f3e5cc3b456eb075f12cf73 # v0.20 - with: - source: "./phlex-src" - clangFormatVersion: 20 - extensions: cpp,hpp,cpp.in,hpp.in - continue-on-error: true + - name: Run clang-format lint + id: lint + uses: DoozyX/clang-format-lint-action@bcb4eb2cb0d707ee4f3e5cc3b456eb075f12cf73 # v0.20 + with: + source: "./phlex-src" + clangFormatVersion: 20 + extensions: cpp,hpp,cpp.in,hpp.in + continue-on-error: true - - name: Evaluate clang-format result - if: always() && steps.lint.outcome != 'skipped' - run: | - if [ "${{ steps.lint.outcome }}" = 'success' ]; then - echo "✅ clang-format check passed." - else - echo "::error::clang-format check failed. Please review the output above for details." - echo "::error::Comment '@${{ github.event.repository.name }}bot format' on the PR to attempt auto-fix." - exit 1 - fi + - name: Evaluate clang-format result + if: always() && steps.lint.outcome != 'skipped' + run: | + if [ "${{ steps.lint.outcome }}" = 'success' ]; then + echo "✅ clang-format check passed." + else + echo "::error::clang-format check failed. Please review the output above for details." + echo "::error::Comment '@${{ github.event.repository.name }}bot format' on the PR to attempt auto-fix." + exit 1 + fi clang-format-check-skipped: needs: [pre-check, detect-changes] @@ -124,5 +124,5 @@ jobs: contents: read steps: - - name: No relevant C++ changes detected - run: echo "::notice::No clang-format relevant changes detected; check skipped." + - name: No relevant C++ changes detected + run: echo "::notice::No clang-format relevant changes detected; check skipped." diff --git a/.github/workflows/clang-tidy-check.yaml b/.github/workflows/clang-tidy-check.yaml index 22661c678..42b65c19e 100644 --- a/.github/workflows/clang-tidy-check.yaml +++ b/.github/workflows/clang-tidy-check.yaml @@ -1,11 +1,10 @@ name: Clang-Tidy Check -'run-name': "${{ github.actor }} checking C++ code with clang-tidy" +"run-name": "${{ github.actor }} checking C++ code with clang-tidy" permissions: contents: read pull-requests: read - on: pull_request: issue_comment: @@ -60,34 +59,34 @@ jobs: has_changes: ${{ steps.filter.outputs.matched }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: phlex-src - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ needs.pre-check.outputs.ref }} - file-type: | - cpp - cmake - - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No clang-tidy relevant changes detected; job will be skipped." - else - echo "::group::Clang-tidy relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} + + - name: Detect relevant changes + id: filter + uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + with: + repo-path: phlex-src + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ needs.pre-check.outputs.ref }} + file-type: | + cpp + cmake + + - name: Report detection outcome + run: | + if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then + echo "::notice::No clang-tidy relevant changes detected; job will be skipped." + else + echo "::group::Clang-tidy relevant files" + printf '%s\n' "${{ steps.filter.outputs.matched_files }}" + echo "::endgroup::" + fi clang-tidy-check: needs: [pre-check, detect-changes] @@ -101,77 +100,76 @@ jobs: ) ) runs-on: ubuntu-24.04 - container: image: ghcr.io/framework-r-d/phlex-ci:latest steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ needs.pre-check.outputs.ref }} - path: phlex-src - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Setup build environment - uses: Framework-R-D/phlex/.github/actions/setup-build-env@main - - - name: Configure CMake (Debug) - uses: Framework-R-D/phlex/.github/actions/configure-cmake@main - with: - build-type: Debug - extra-options: "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_SCAN_FOR_MODULES=OFF -DCMAKE_CXX_CLANG_TIDY='clang-tidy;--export-fixes=clang-tidy-fixes.yaml'" + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.pre-check.outputs.ref }} + path: phlex-src + repository: ${{ needs.pre-check.outputs.repo }} - - name: Run clang-tidy using CMake - id: tidy - shell: bash - run: | - . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" + - name: Setup build environment + uses: Framework-R-D/phlex/.github/actions/setup-build-env@main - echo "➡️ Running clang-tidy checks..." - cmake_status=0 - cmake --build . -j "$(nproc)" > clang-tidy.log 2>&1 || cmake_status=$? + - name: Configure CMake (Debug) + uses: Framework-R-D/phlex/.github/actions/configure-cmake@main + with: + build-type: Debug + extra-options: "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_SCAN_FOR_MODULES=OFF -DCMAKE_CXX_CLANG_TIDY='clang-tidy;--export-fixes=clang-tidy-fixes.yaml'" - # Distinguish tooling failures from issue detection by checking log content - if [ "$cmake_status" -ne 0 ]; then - if ! grep -qE '^/.+\.(cpp|hpp|c|h):[0-9]+:[0-9]+: (warning|error):' clang-tidy.log; then - echo "::error::clang-tidy failed without producing diagnostic output (exit code $cmake_status)" - echo "::group::clang-tidy log output" - cat clang-tidy.log - echo "::endgroup::" - exit "$cmake_status" + - name: Run clang-tidy using CMake + id: tidy + shell: bash + run: | + . /entrypoint.sh + cd "$GITHUB_WORKSPACE/phlex-build" + + echo "➡️ Running clang-tidy checks..." + cmake_status=0 + cmake --build . -j "$(nproc)" > clang-tidy.log 2>&1 || cmake_status=$? + + # Distinguish tooling failures from issue detection by checking log content + if [ "$cmake_status" -ne 0 ]; then + if ! grep -qE '^/.+\.(cpp|hpp|c|h):[0-9]+:[0-9]+: (warning|error):' clang-tidy.log; then + echo "::error::clang-tidy failed without producing diagnostic output (exit code $cmake_status)" + echo "::group::clang-tidy log output" + cat clang-tidy.log + echo "::endgroup::" + exit "$cmake_status" + fi fi - fi - if grep -qE '^/.+\.(cpp|hpp|c|h):[0-9]+:[0-9]+: (warning|error):' clang-tidy.log; then - echo "has_issues=true" >> "$GITHUB_OUTPUT" - echo "::warning::Clang-tidy found issues in the code" - echo "Error count by check (full details in clang-tidy-log artifact):" - sed -nEe '\&^/& s&^.*\[([^][:space:]]+)\]$&\1&p' clang-tidy.log | sort | uniq -c | sort -n -k 1 -r - echo "Comment '@${{ github.event.repository.name }}bot tidy-fix [...]' on the PR to attempt auto-fix" - else - echo "has_issues=false" >> "$GITHUB_OUTPUT" - echo "✅ clang-tidy check passed" - fi + if grep -qE '^/.+\.(cpp|hpp|c|h):[0-9]+:[0-9]+: (warning|error):' clang-tidy.log; then + echo "has_issues=true" >> "$GITHUB_OUTPUT" + echo "::warning::Clang-tidy found issues in the code" + echo "Error count by check (full details in clang-tidy-log artifact):" + sed -nEe '\&^/& s&^.*\[([^][:space:]]+)\]$&\1&p' clang-tidy.log | sort | uniq -c | sort -n -k 1 -r + echo "Comment '@${{ github.event.repository.name }}bot tidy-fix [...]' on the PR to attempt auto-fix" + else + echo "has_issues=false" >> "$GITHUB_OUTPUT" + echo "✅ clang-tidy check passed" + fi - - name: Upload clang-tidy report - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: clang-tidy-report - path: phlex-build/clang-tidy-fixes.yaml - retention-days: 7 - if-no-files-found: ignore + - name: Upload clang-tidy report + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: clang-tidy-report + path: phlex-build/clang-tidy-fixes.yaml + retention-days: 7 + if-no-files-found: ignore - - name: Upload clang-tidy log - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: clang-tidy-log - path: phlex-build/clang-tidy.log - retention-days: 7 - if-no-files-found: ignore + - name: Upload clang-tidy log + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: clang-tidy-log + path: phlex-build/clang-tidy.log + retention-days: 7 + if-no-files-found: ignore clang-tidy-pr-comments: needs: [pre-check, clang-tidy-check] @@ -199,7 +197,7 @@ jobs: if: github.event_name == 'issue_comment' uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: - node-version: '20.x' + node-version: "20.x" - name: Install js-yaml if: github.event_name == 'issue_comment' @@ -282,5 +280,5 @@ jobs: contents: read steps: - - name: No relevant clang-tidy changes detected - run: echo "::notice::No clang-tidy relevant changes detected; check skipped." + - name: No relevant clang-tidy changes detected + run: echo "::notice::No clang-tidy relevant changes detected; check skipped." diff --git a/.github/workflows/clang-tidy-fix.yaml b/.github/workflows/clang-tidy-fix.yaml index d77ee5fa0..9036f44a8 100644 --- a/.github/workflows/clang-tidy-fix.yaml +++ b/.github/workflows/clang-tidy-fix.yaml @@ -1,5 +1,5 @@ name: Clang-Tidy Fix -'run-name': "${{ github.actor }} fixing C++ code with clang-tidy" +"run-name": "${{ github.actor }} fixing C++ code with clang-tidy" on: issue_comment: diff --git a/.github/workflows/cmake-build.yaml b/.github/workflows/cmake-build.yaml index 310f79a41..8ccbb043e 100644 --- a/.github/workflows/cmake-build.yaml +++ b/.github/workflows/cmake-build.yaml @@ -6,7 +6,7 @@ on: issue_comment: types: [created] push: - branches: [ main, develop ] + branches: [main, develop] workflow_dispatch: inputs: ref: @@ -23,7 +23,7 @@ on: - `+clang/none +clang/valgrind` (run default matrix plus specified) Default (if empty): Run all except clang/none and clang/valgrind. required: false - default: '' + default: "" workflow_call: inputs: checkout-path: @@ -192,77 +192,77 @@ jobs: image: ghcr.io/framework-r-d/phlex-ci:latest steps: - - name: Check out code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + - name: Check out code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + path: ${{ env.local_checkout_path }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Setup build environment - uses: Framework-R-D/phlex/.github/actions/setup-build-env@main - with: - build-path: ${{ env.local_build_path }} + - name: Setup build environment + uses: Framework-R-D/phlex/.github/actions/setup-build-env@main + with: + build-path: ${{ env.local_build_path }} - - name: Announce CMake configuration - run: echo "➡️ Configuring CMake..." + - name: Announce CMake configuration + run: echo "➡️ Configuring CMake..." - - name: Extract repository name - id: repo_name - env: - REPO_FULL_NAME: ${{ needs.pre-check.outputs.repo }} - run: echo "name=${REPO_FULL_NAME##*/}" >> "$GITHUB_OUTPUT" + - name: Extract repository name + id: repo_name + env: + REPO_FULL_NAME: ${{ needs.pre-check.outputs.repo }} + run: echo "name=${REPO_FULL_NAME##*/}" >> "$GITHUB_OUTPUT" - - name: Configure CMake - id: configure - uses: Framework-R-D/phlex/.github/actions/configure-cmake@main - with: - source-path: ${{ env.local_checkout_path }} - build-path: ${{ env.local_build_path }} - build-type: ${{ env.BUILD_TYPE }} - cpp-compiler: ${{ matrix.compiler == 'gcc' && 'g++' || 'clang++' }} - extra-options: | - ${{ matrix.sanitizer == 'asan' && format('-D{0}_ENABLE_ASAN=ON', steps.repo_name.outputs.name) || '' }} - ${{ matrix.sanitizer == 'tsan' && format('-D{0}_ENABLE_TSAN=ON', steps.repo_name.outputs.name) || '' }} + - name: Configure CMake + id: configure + uses: Framework-R-D/phlex/.github/actions/configure-cmake@main + with: + source-path: ${{ env.local_checkout_path }} + build-path: ${{ env.local_build_path }} + build-type: ${{ env.BUILD_TYPE }} + cpp-compiler: ${{ matrix.compiler == 'gcc' && 'g++' || 'clang++' }} + extra-options: | + ${{ matrix.sanitizer == 'asan' && format('-D{0}_ENABLE_ASAN=ON', steps.repo_name.outputs.name) || '' }} + ${{ matrix.sanitizer == 'tsan' && format('-D{0}_ENABLE_TSAN=ON', steps.repo_name.outputs.name) || '' }} - - name: Build - id: build - uses: Framework-R-D/phlex/.github/actions/build-cmake@main - with: - build-path: ${{ env.local_build_path }} + - name: Build + id: build + uses: Framework-R-D/phlex/.github/actions/build-cmake@main + with: + build-path: ${{ env.local_build_path }} - - name: Run tests - if: matrix.sanitizer != 'valgrind' - run: | - . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${{ env.local_build_path }}" + - name: Run tests + if: matrix.sanitizer != 'valgrind' + run: | + . /entrypoint.sh + cd "$GITHUB_WORKSPACE/${{ env.local_build_path }}" - echo "➡️ Running tests..." - echo "::group::Running ctest" - 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 + echo "➡️ Running tests..." + echo "::group::Running ctest" + 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: Run Valgrind tests - if: matrix.sanitizer == 'valgrind' - run: | - . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${{ env.local_build_path }}" + - name: Run Valgrind tests + if: matrix.sanitizer == 'valgrind' + run: | + . /entrypoint.sh + cd "$GITHUB_WORKSPACE/${{ env.local_build_path }}" - echo "➡️ Running Valgrind tests..." - echo "::group::Running ctest -T memcheck" - if ctest -T memcheck; then - echo "::endgroup::" - echo "✅ Valgrind tests passed." - else - echo "::endgroup::" - echo "⚠️ Valgrind tests failed, but the workflow will continue." - fi + echo "➡️ Running Valgrind tests..." + echo "::group::Running ctest -T memcheck" + if ctest -T memcheck; then + echo "::endgroup::" + echo "✅ Valgrind tests passed." + else + echo "::endgroup::" + echo "⚠️ Valgrind tests failed, but the workflow will continue." + fi cmake-build-skipped: needs: [pre-check, detect-changes] @@ -280,8 +280,8 @@ jobs: contents: read steps: - - name: No relevant C++ or CMake changes detected - run: echo "::notice::No relevant C++ or CMake changes detected; build skipped." + - name: No relevant C++ or CMake changes detected + run: echo "::notice::No relevant C++ or CMake changes detected; build skipped." build-complete: needs: [pre-check, build] @@ -299,11 +299,11 @@ jobs: with: message: | Build and test workflow completed. - + **Result:** ${{ needs.build.result == 'success' && '✅ All builds and tests passed.' || needs.build.result == 'failure' && '❌ Some builds or tests failed.' || needs.build.result == 'cancelled' && '⚠️ Build was cancelled before completion.' || needs.build.result == 'skipped' && 'ℹ️ Build job was skipped.' || format('ℹ️ Build job completed with status: {0}.', needs.build.result) }} - + See the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for detailed results. diff --git a/.github/workflows/cmake-format-check.yaml b/.github/workflows/cmake-format-check.yaml index 09c9b40f2..440465f45 100644 --- a/.github/workflows/cmake-format-check.yaml +++ b/.github/workflows/cmake-format-check.yaml @@ -73,32 +73,32 @@ jobs: outputs: has_changes: ${{ steps.filter.outputs.matched }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + path: ${{ env.local_checkout_path }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} - file-type: cmake + - name: Detect relevant changes + id: filter + uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + with: + repo-path: ${{ env.local_checkout_path }} + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} + file-type: cmake - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No CMake-related changes detected; formatting check will be skipped." - else - echo "::group::CMake-related files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + - name: Report detection outcome + run: | + if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then + echo "::notice::No CMake-related changes detected; formatting check will be skipped." + else + echo "::group::CMake-related files" + printf '%s\n' "${{ steps.filter.outputs.matched_files }}" + echo "::endgroup::" + fi cmake-format-check: needs: [pre-check, detect-changes] @@ -112,40 +112,39 @@ jobs: ) ) runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ needs.pre-check.outputs.ref }} - path: ${{ env.local_checkout_path }} - repository: ${{ needs.pre-check.outputs.repo }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.pre-check.outputs.ref }} + path: ${{ env.local_checkout_path }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: '3.x' + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.x" - - name: Install gersemi - run: pip install gersemi + - name: Install gersemi + run: pip install gersemi - - name: Check CMake formatting - id: lint - run: | - echo "➡️ Checking CMake file formatting..." - gersemi --check ${{ env.local_checkout_path }} - continue-on-error: true + - name: Check CMake formatting + id: lint + run: | + echo "➡️ Checking CMake file formatting..." + gersemi --check ${{ env.local_checkout_path }} + continue-on-error: true - - name: Evaluate CMake formatting result - if: always() && steps.lint.outcome != 'skipped' - run: | - if [ "${{ steps.lint.outcome }}" = 'success' ]; then - echo "✅ All CMake files are properly formatted." - else - echo "::error::Found files with formatting issues." - echo "::error::Run 'gersemi -i ' locally or comment '@${{ github.event.repository.name }}bot format' on the PR to auto-fix." - exit 1 - fi + - name: Evaluate CMake formatting result + if: always() && steps.lint.outcome != 'skipped' + run: | + if [ "${{ steps.lint.outcome }}" = 'success' ]; then + echo "✅ All CMake files are properly formatted." + else + echo "::error::Found files with formatting issues." + echo "::error::Run 'gersemi -i ' locally or comment '@${{ github.event.repository.name }}bot format' on the PR to auto-fix." + exit 1 + fi cmake-format-check-skipped: needs: [pre-check, detect-changes] @@ -159,7 +158,6 @@ jobs: needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') runs-on: ubuntu-latest - steps: - - name: No relevant CMake changes detected - run: echo "::notice::No CMake-related changes detected; cmake-format check skipped." + - name: No relevant CMake changes detected + run: echo "::notice::No CMake-related changes detected; cmake-format check skipped." diff --git a/.github/workflows/cmake-format-fix.yaml b/.github/workflows/cmake-format-fix.yaml index 6aac9e336..327e88a6c 100644 --- a/.github/workflows/cmake-format-fix.yaml +++ b/.github/workflows/cmake-format-fix.yaml @@ -79,7 +79,7 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.x' + python-version: "3.x" - name: Install gersemi run: pip install gersemi diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index bcba9a316..691aa3990 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -3,10 +3,10 @@ name: "CodeQL Analysis" on: push: - branches: [ main, develop ] + branches: [main, develop] pull_request: schedule: - - cron: '0 3 * * 0' # weekly (UTC) — adjust as needed + - cron: "0 3 * * 0" # weekly (UTC) — adjust as needed workflow_dispatch: inputs: ref: @@ -215,7 +215,13 @@ jobs: fi determine-languages: - needs: [pre-check, detect-changes-cpp, detect-changes-python, detect-changes-actions] + needs: + [ + pre-check, + detect-changes-cpp, + detect-changes-python, + detect-changes-actions, + ] if: always() && needs.pre-check.result == 'success' runs-on: ubuntu-latest outputs: diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index e879a77ad..6ff62aa0c 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -4,7 +4,7 @@ run-name: "${{ github.actor }} running code coverage" on: push: - branches: [ main, develop ] + branches: [main, develop] pull_request: issue_comment: types: [created] @@ -15,13 +15,13 @@ on: required: false type: string phlex-coverage-compiler: - description: 'Compiler to use for coverage build (gcc or clang)' + description: "Compiler to use for coverage build (gcc or clang)" required: false - default: 'clang' + default: "clang" phlex-enable-form: - description: 'Enable FORM integration (set to OFF to exclude FORM sources)' + description: "Enable FORM integration (set to OFF to exclude FORM sources)" required: false - default: 'ON' + default: "ON" permissions: contents: read @@ -71,35 +71,35 @@ jobs: outputs: has_changes: ${{ steps.filter.outputs.matched }} steps: - - name: Check out source code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: phlex-src - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: phlex-src - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ needs.pre-check.outputs.ref }} - file-type: | - cpp - cmake - python - - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No coverage relevant changes detected; workflow will be skipped." - else - echo "::group::Coverage relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + - name: Check out source code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} + + - name: Detect relevant changes + id: filter + uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + with: + repo-path: phlex-src + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ needs.pre-check.outputs.ref }} + file-type: | + cpp + cmake + python + + - name: Report detection outcome + run: | + if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then + echo "::notice::No coverage relevant changes detected; workflow will be skipped." + else + echo "::group::Coverage relevant files" + printf '%s\n' "${{ steps.filter.outputs.matched_files }}" + echo "::endgroup::" + fi coverage: needs: [pre-check, detect-changes] @@ -113,7 +113,6 @@ jobs: ) ) runs-on: ubuntu-24.04 - container: image: ghcr.io/framework-r-d/phlex-ci:latest @@ -127,237 +126,237 @@ jobs: artifact_upload_available: ${{ steps.artifact_runtime.outputs.available }} steps: - - name: Determine coverage options - id: coverage_options - shell: bash - run: | - set -euo pipefail - requested_compiler="${{ github.event_name == 'workflow_dispatch' && github.event.inputs['phlex-coverage-compiler'] || '' }}" - default_compiler="clang" - requested_form="${{ github.event_name == 'workflow_dispatch' && github.event.inputs['phlex-enable-form'] || '' }}" - default_form="ON" - compiler="${requested_compiler:-$default_compiler}" - form="${requested_form:-$default_form}" - echo "compiler=$compiler" >> "$GITHUB_OUTPUT" - echo "enable_form=$form" >> "$GITHUB_OUTPUT" - - - name: Check out source code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ needs.pre-check.outputs.ref }} - path: phlex-src - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Setup build environment - uses: Framework-R-D/phlex/.github/actions/setup-build-env@main - - - 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 - 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 - 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' }} - run: | - echo "ERROR: Unknown compiler '${{ steps.coverage_options.outputs.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 - - - name: Run tests with coverage - run: | - . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" - - echo "➡️ Running tests with coverage..." - PROFILE_ROOT="$GITHUB_WORKSPACE/phlex-build/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 - run: | - . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" - - 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 - run: | - . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" - - 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." + - name: Determine coverage options + id: coverage_options + shell: bash + run: | + set -euo pipefail + requested_compiler="${{ github.event_name == 'workflow_dispatch' && github.event.inputs['phlex-coverage-compiler'] || '' }}" + default_compiler="clang" + requested_form="${{ github.event_name == 'workflow_dispatch' && github.event.inputs['phlex-enable-form'] || '' }}" + default_form="ON" + compiler="${requested_compiler:-$default_compiler}" + form="${requested_form:-$default_form}" + echo "compiler=$compiler" >> "$GITHUB_OUTPUT" + echo "enable_form=$form" >> "$GITHUB_OUTPUT" + + - name: Check out source code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.pre-check.outputs.ref }} + path: phlex-src + repository: ${{ needs.pre-check.outputs.repo }} + + - name: Setup build environment + uses: Framework-R-D/phlex/.github/actions/setup-build-env@main + + - 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 + 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 + 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' }} + run: | + echo "ERROR: Unknown compiler '${{ steps.coverage_options.outputs.compiler }}'. Must be 'gcc' or 'clang'." exit 1 - fi - - - name: Generate Python coverage report - id: report_python - if: ${{ steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped' }} - shell: bash - run: | - . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" - - 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 + + - 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 + + - name: Run tests with coverage + run: | + . /entrypoint.sh + cd "$GITHUB_WORKSPACE/phlex-build" + + echo "➡️ Running tests with coverage..." + PROFILE_ROOT="$GITHUB_WORKSPACE/phlex-build/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 + run: | + . /entrypoint.sh + cd "$GITHUB_WORKSPACE/phlex-build" + + 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 "✅ Python coverage report generation succeeded." + 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 + run: | + . /entrypoint.sh + cd "$GITHUB_WORKSPACE/phlex-build" + + 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 "::warning::Python coverage report generation failed (non-fatal)." + echo "::error::Clang coverage report generation failed." + exit 1 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 - run: | - cd "$GITHUB_WORKSPACE/phlex-build" - 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 - run: | - set -euo pipefail - ARTIFACT_DIR="$GITHUB_WORKSPACE/coverage-artifacts" - rm -rf "$ARTIFACT_DIR" - mkdir -p "$ARTIFACT_DIR" - cd "$GITHUB_WORKSPACE/phlex-build" - 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/" + + - name: Generate Python coverage report + id: report_python + if: ${{ steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped' }} + shell: bash + run: | + . /entrypoint.sh + cd "$GITHUB_WORKSPACE/phlex-build" + + 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 - 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 - 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 - - - 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 + + - name: Capture coverage artifact availability + id: coverage_outputs + if: ${{ steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped' }} + shell: bash + run: | + cd "$GITHUB_WORKSPACE/phlex-build" + 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 + run: | + set -euo pipefail + ARTIFACT_DIR="$GITHUB_WORKSPACE/coverage-artifacts" + rm -rf "$ARTIFACT_DIR" + mkdir -p "$ARTIFACT_DIR" + cd "$GITHUB_WORKSPACE/phlex-build" + 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 + 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 + + - 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 @@ -367,65 +366,65 @@ jobs: 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@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 - 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 + - 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@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + 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 coverage-skipped: needs: [pre-check, detect-changes] @@ -439,5 +438,5 @@ jobs: contents: read steps: - - name: No relevant coverage changes detected - run: echo "::notice::No relevant C++ changes detected; coverage workflow skipped." + - name: No relevant coverage changes detected + run: echo "::notice::No relevant C++ changes detected; coverage workflow skipped." diff --git a/.github/workflows/header-guards-check.yaml b/.github/workflows/header-guards-check.yaml index 995c17555..ca5ab501c 100644 --- a/.github/workflows/header-guards-check.yaml +++ b/.github/workflows/header-guards-check.yaml @@ -74,32 +74,32 @@ jobs: outputs: has_changes: ${{ steps.filter.outputs.matched }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + path: ${{ env.local_checkout_path }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} - file-type: cpp + - name: Detect relevant changes + id: filter + uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + with: + repo-path: ${{ env.local_checkout_path }} + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} + file-type: cpp - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No header file changes detected; check will be skipped." - else - echo "::group::Header files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + - name: Report detection outcome + run: | + if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then + echo "::notice::No header file changes detected; check will be skipped." + else + echo "::group::Header files" + printf '%s\n' "${{ steps.filter.outputs.matched_files }}" + echo "::endgroup::" + fi header-guards-check: needs: [pre-check, detect-changes] @@ -117,31 +117,31 @@ jobs: contents: read steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ needs.pre-check.outputs.ref }} - path: ${{ env.local_checkout_path }} - repository: ${{ needs.pre-check.outputs.repo }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.pre-check.outputs.ref }} + path: ${{ env.local_checkout_path }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: '3.12' + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.12" - - name: Check header guards - id: check - working-directory: ${{ env.local_checkout_path }} - run: | - python3 scripts/fix_header_guards.py --check --root . phlex plugins form > check_output.txt 2>&1 || echo "has_issues=true" >> "$GITHUB_OUTPUT" - cat check_output.txt + - name: Check header guards + id: check + working-directory: ${{ env.local_checkout_path }} + run: | + python3 scripts/fix_header_guards.py --check --root . phlex plugins form > check_output.txt 2>&1 || echo "has_issues=true" >> "$GITHUB_OUTPUT" + cat check_output.txt - - name: Report results - if: always() && steps.check.outputs.has_issues == 'true' - run: | - echo "::error::Header guard check failed." - echo "::error::Comment '@${{ github.event.repository.name }}bot header-guards-fix' on the PR to auto-fix." - exit 1 + - name: Report results + if: always() && steps.check.outputs.has_issues == 'true' + run: | + echo "::error::Header guard check failed." + echo "::error::Comment '@${{ github.event.repository.name }}bot header-guards-fix' on the PR to auto-fix." + exit 1 header-guards-check-skipped: needs: [pre-check, detect-changes] @@ -159,5 +159,5 @@ jobs: contents: read steps: - - name: No relevant header changes detected - run: echo "::notice::No header file changes detected; header-guards-check skipped." + - name: No relevant header changes detected + run: echo "::notice::No header file changes detected; header-guards-check skipped." diff --git a/.github/workflows/header-guards-fix.yaml b/.github/workflows/header-guards-fix.yaml index 25254ce36..662b910e5 100644 --- a/.github/workflows/header-guards-fix.yaml +++ b/.github/workflows/header-guards-fix.yaml @@ -79,7 +79,7 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.12' + python-version: "3.12" - name: Fix header guards working-directory: ${{ env.local_checkout_path }} diff --git a/.github/workflows/jsonnet-format-check.yaml b/.github/workflows/jsonnet-format-check.yaml index c23020923..0cfed1fac 100644 --- a/.github/workflows/jsonnet-format-check.yaml +++ b/.github/workflows/jsonnet-format-check.yaml @@ -74,32 +74,32 @@ jobs: outputs: has_changes: ${{ steps.filter.outputs.matched }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + path: ${{ env.local_checkout_path }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} - file-type: jsonnet + - name: Detect relevant changes + id: filter + uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + with: + repo-path: ${{ env.local_checkout_path }} + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} + file-type: jsonnet - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No Jsonnet-related changes detected; formatting check will be skipped." - else - echo "::group::Jsonnet-related files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + - name: Report detection outcome + run: | + if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then + echo "::notice::No Jsonnet-related changes detected; formatting check will be skipped." + else + echo "::group::Jsonnet-related files" + printf '%s\n' "${{ steps.filter.outputs.matched_files }}" + echo "::endgroup::" + fi jsonnet-format-check: needs: [pre-check, detect-changes] @@ -117,31 +117,31 @@ jobs: image: public.ecr.aws/bitnami/jsonnet:latest steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - path: ${{ env.local_checkout_path }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} + path: ${{ env.local_checkout_path }} - - name: Check Jsonnet formatting - id: lint - working-directory: ${{ env.local_checkout_path }} - run: | - find . \( -name "*.jsonnet" -o -name "*.libsonnet" \) -print0 | xargs -0 -r -I {} \ - bash -c 'jsonnetfmt --test "{}" || (echo "FAILED: {}" && diff -u <(jsonnetfmt "{}") "{}" && exit 1)' - continue-on-error: true + - name: Check Jsonnet formatting + id: lint + working-directory: ${{ env.local_checkout_path }} + run: | + find . \( -name "*.jsonnet" -o -name "*.libsonnet" \) -print0 | xargs -0 -r -I {} \ + bash -c 'jsonnetfmt --test "{}" || (echo "FAILED: {}" && diff -u <(jsonnetfmt "{}") "{}" && exit 1)' + continue-on-error: true - - name: Evaluate Jsonnet formatting result - if: always() && steps.lint.outcome != 'skipped' - run: | - if [ "${{ steps.lint.outcome }}" = 'success' ]; then - echo "✅ Jsonnet formatting check passed." - else - echo "::error::Jsonnet formatting issues found. Please review the output above for details." - echo "::error::Run 'jsonnetfmt -i ' locally or comment '@${{ github.event.repository.name }}bot format' on the PR to auto-fix." - exit 1 - fi + - name: Evaluate Jsonnet formatting result + if: always() && steps.lint.outcome != 'skipped' + run: | + if [ "${{ steps.lint.outcome }}" = 'success' ]; then + echo "✅ Jsonnet formatting check passed." + else + echo "::error::Jsonnet formatting issues found. Please review the output above for details." + echo "::error::Run 'jsonnetfmt -i ' locally or comment '@${{ github.event.repository.name }}bot format' on the PR to auto-fix." + exit 1 + fi jsonnet-format-check-skipped: needs: [pre-check, detect-changes] @@ -155,7 +155,6 @@ jobs: needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') runs-on: ubuntu-latest - steps: - - name: No relevant Jsonnet changes detected - run: echo "::notice::No Jsonnet-related changes detected; jsonnet-format check skipped." + - name: No relevant Jsonnet changes detected + run: echo "::notice::No Jsonnet-related changes detected; jsonnet-format check skipped." diff --git a/.github/workflows/markdown-check.yaml b/.github/workflows/markdown-check.yaml index 1c340faaa..f4412dd99 100644 --- a/.github/workflows/markdown-check.yaml +++ b/.github/workflows/markdown-check.yaml @@ -74,32 +74,32 @@ jobs: outputs: has_changes: ${{ steps.filter.outputs.matched }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + path: ${{ env.local_checkout_path }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} - file-type: md + - name: Detect relevant changes + id: filter + uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + with: + repo-path: ${{ env.local_checkout_path }} + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} + file-type: md - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No Markdown-related changes detected; formatting check will be skipped." - else - echo "::group::Markdown-related files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + - name: Report detection outcome + run: | + if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then + echo "::notice::No Markdown-related changes detected; formatting check will be skipped." + else + echo "::group::Markdown-related files" + printf '%s\n' "${{ steps.filter.outputs.matched_files }}" + echo "::endgroup::" + fi markdown-check: needs: [pre-check, detect-changes] @@ -117,35 +117,35 @@ jobs: contents: read steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ needs.pre-check.outputs.ref }} - path: ${{ env.local_checkout_path }} - repository: ${{ needs.pre-check.outputs.repo }} + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.pre-check.outputs.ref }} + path: ${{ env.local_checkout_path }} + repository: ${{ needs.pre-check.outputs.repo }} - - name: Add problem matcher - uses: xt0rted/markdownlint-problem-matcher@1a5fabfb577370cfdf5af944d418e4be3ea06f27 # v3.0.0 + - name: Add problem matcher + uses: xt0rted/markdownlint-problem-matcher@1a5fabfb577370cfdf5af944d418e4be3ea06f27 # v3.0.0 - - name: Run markdownlint - id: lint - uses: DavidAnson/markdownlint-cli2-action@07035fd053f7be764496c0f8d8f9f41f98305101 # v22.0.0 - with: - globs: | - ${{ env.local_checkout_path }}/**/*.md - !${{ env.local_checkout_path }}/**/CHANGELOG.md - continue-on-error: true + - name: Run markdownlint + id: lint + uses: DavidAnson/markdownlint-cli2-action@07035fd053f7be764496c0f8d8f9f41f98305101 # v22.0.0 + with: + globs: | + ${{ env.local_checkout_path }}/**/*.md + !${{ env.local_checkout_path }}/**/CHANGELOG.md + continue-on-error: true - - name: Evaluate markdownlint result - if: always() && steps.lint.outcome != 'skipped' - run: | - if [ "${{ steps.lint.outcome }}" = "success" ]; then - echo "✅ Markdown formatting check passed." - else - echo "::error::Markdown formatting check failed." - echo "::error::Comment '@${{ github.event.repository.name }}bot format' or '@${{ github.event.repository.name }}bot markdown-fix' on the PR to auto-fix." - exit 1 - fi + - name: Evaluate markdownlint result + if: always() && steps.lint.outcome != 'skipped' + run: | + if [ "${{ steps.lint.outcome }}" = "success" ]; then + echo "✅ Markdown formatting check passed." + else + echo "::error::Markdown formatting check failed." + echo "::error::Comment '@${{ github.event.repository.name }}bot format' or '@${{ github.event.repository.name }}bot markdown-fix' on the PR to auto-fix." + exit 1 + fi markdown-check-skipped: needs: [pre-check, detect-changes] @@ -163,5 +163,5 @@ jobs: contents: read steps: - - name: No relevant Markdown changes detected - run: echo "::notice::No Markdown-related changes detected; markdown-check skipped." + - name: No relevant Markdown changes detected + run: echo "::notice::No Markdown-related changes detected; markdown-check skipped." diff --git a/.github/workflows/markdown-fix.yaml b/.github/workflows/markdown-fix.yaml index 9261e2f65..ac865797a 100644 --- a/.github/workflows/markdown-fix.yaml +++ b/.github/workflows/markdown-fix.yaml @@ -79,7 +79,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: - node-version: '20' + node-version: "20" - name: Install markdownlint-cli2 run: npm install -g markdownlint-cli2 diff --git a/.github/workflows/python-check.yaml b/.github/workflows/python-check.yaml index daecf149c..754830566 100644 --- a/.github/workflows/python-check.yaml +++ b/.github/workflows/python-check.yaml @@ -74,32 +74,32 @@ jobs: outputs: has_changes: ${{ steps.filter.outputs.matched }} steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - path: ${{ env.local_checkout_path }} - ref: ${{ needs.pre-check.outputs.ref }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Detect relevant changes - id: filter - uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main - with: - repo-path: ${{ env.local_checkout_path }} - base-ref: ${{ needs.pre-check.outputs.base_sha }} - head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} - file-type: python - - - name: Report detection outcome - run: | - if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then - echo "::notice::No python check relevant changes detected; job will be skipped." - else - echo "::group::Python check relevant files" - printf '%s\n' "${{ steps.filter.outputs.matched_files }}" - echo "::endgroup::" - fi + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + path: ${{ env.local_checkout_path }} + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} + + - name: Detect relevant changes + id: filter + uses: Framework-R-D/phlex/.github/actions/detect-relevant-changes@main + with: + repo-path: ${{ env.local_checkout_path }} + base-ref: ${{ needs.pre-check.outputs.base_sha }} + head-ref: ${{ (github.event_name == 'workflow_call' && inputs.pr-head-sha) || needs.pre-check.outputs.ref }} + file-type: python + + - name: Report detection outcome + run: | + if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then + echo "::notice::No python check relevant changes detected; job will be skipped." + else + echo "::group::Python check relevant files" + printf '%s\n' "${{ steps.filter.outputs.matched_files }}" + echo "::endgroup::" + fi python-check: needs: [pre-check, detect-changes] @@ -117,49 +117,49 @@ jobs: contents: read steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ needs.pre-check.outputs.ref }} - path: ${{ env.local_checkout_path }} - repository: ${{ needs.pre-check.outputs.repo }} - - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: '3.x' - - - name: Install Python dependencies - run: | - pip install ruff mypy - - - name: Run ruff check - id: ruff - working-directory: ${{ env.local_checkout_path }} - env: - FORCE_COLOR: 1 - run: | - echo "➡️ Checking Python code with ruff..." - ruff check - continue-on-error: true - - - name: Run MyPy check - id: mypy - working-directory: ${{ env.local_checkout_path }} - run: | - echo "➡️ Checking Python code with MyPy..." - mypy --color-output . - continue-on-error: true - - - name: Evaluate Python checks result - if: always() && (steps.ruff.outcome != 'skipped' || steps.mypy.outcome != 'skipped') - run: | - if [ "${{ steps.ruff.outcome }}" = 'success' ] && [ "${{ steps.mypy.outcome }}" = 'success' ]; then - echo "✅ Python checks passed." - else - echo "::error::Python checks failed. Comment '@${{ github.event.repository.name }}bot python-fix' on the PR to attempt auto-fix." - exit 1 - fi + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.pre-check.outputs.ref }} + path: ${{ env.local_checkout_path }} + repository: ${{ needs.pre-check.outputs.repo }} + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.x" + + - name: Install Python dependencies + run: | + pip install ruff mypy + + - name: Run ruff check + id: ruff + working-directory: ${{ env.local_checkout_path }} + env: + FORCE_COLOR: 1 + run: | + echo "➡️ Checking Python code with ruff..." + ruff check + continue-on-error: true + + - name: Run MyPy check + id: mypy + working-directory: ${{ env.local_checkout_path }} + run: | + echo "➡️ Checking Python code with MyPy..." + mypy --color-output . + continue-on-error: true + + - name: Evaluate Python checks result + if: always() && (steps.ruff.outcome != 'skipped' || steps.mypy.outcome != 'skipped') + run: | + if [ "${{ steps.ruff.outcome }}" = 'success' ] && [ "${{ steps.mypy.outcome }}" = 'success' ]; then + echo "✅ Python checks passed." + else + echo "::error::Python checks failed. Comment '@${{ github.event.repository.name }}bot python-fix' on the PR to attempt auto-fix." + exit 1 + fi python-check-skipped: needs: [pre-check, detect-changes] @@ -177,5 +177,5 @@ jobs: contents: read steps: - - name: No relevant Python changes detected - run: echo "No Python relevant changes detected; check skipped." + - name: No relevant Python changes detected + run: echo "No Python relevant changes detected; check skipped." diff --git a/.github/workflows/python-fix.yaml b/.github/workflows/python-fix.yaml index 0543afe42..a6ba8a35d 100644 --- a/.github/workflows/python-fix.yaml +++ b/.github/workflows/python-fix.yaml @@ -74,7 +74,7 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.x' + python-version: "3.x" - name: Install Python dependencies run: | diff --git a/ci/spack.yaml b/ci/spack.yaml index c894bbb51..e14f367c2 100644 --- a/ci/spack.yaml +++ b/ci/spack.yaml @@ -1,14 +1,14 @@ spack: specs: - - phlex - - catch2 - - cmake - - lcov - - ninja - - py-gcovr - - py-pip # Needed temporarily for ruff installation - - py-pytest-cov # Needed for Python coverage reports - - | + - phlex + - catch2 + - cmake + - lcov + - ninja + - py-gcovr + - py-pip # Needed temporarily for ruff installation + - py-pytest-cov # Needed for Python coverage reports + - | llvm@21.1.4: +zstd +llvm_dylib +link_llvm_dylib ~lldb targets=x86 view: true @@ -19,34 +19,33 @@ spack: packages: all: target: - - x86_64_v3 + - x86_64_v3 cmake: require: - - "~qtgui" - - "@4:" + - "~qtgui" + - "@4:" phlex: require: - - "+form" - - "cxxstd=23" - - "%gcc@15" - + - "+form" + - "cxxstd=23" + - "%gcc@15" # GCC 15 uses C23 as the default C language, and the versions of # libunwind and unuran available in Spack (as of 8/12/2025) do not # yet support C23. libunwind: require: - - "cflags='-std=c17'" + - "cflags='-std=c17'" unuran: require: - - "cflags='-std=c17'" + - "cflags='-std=c17'" # ROOT should be built with the same version of the C++ standard # as will be used by Phlex (C++20 for now). root: require: - - "~x" # No graphics libraries required - - "cxxstd=23" + - "~x" # No graphics libraries required + - "cxxstd=23" diff --git a/codecov.yml b/codecov.yml index 4c08413ce..8362424dd 100644 --- a/codecov.yml +++ b/codecov.yml @@ -25,15 +25,15 @@ comment: require_head: yes ignore: - - "test/**/*" # Ignore test files - - "**/CMakeFiles/**" # Ignore CMake generated files - - "build/**" # Ignore build directories (generic 1/3) - - "build-*/**" # Ignore build directories (generic 2/3) - - "*-build/**" # Ignore build directories (generic 3/3) + - "test/**/*" # Ignore test files + - "**/CMakeFiles/**" # Ignore CMake generated files + - "build/**" # Ignore build directories (generic 1/3) + - "build-*/**" # Ignore build directories (generic 2/3) + - "*-build/**" # Ignore build directories (generic 3/3) - ".coverage-generated/**" # Ignore normalized symlink tree - - "ci/**" # Ignore CI scripts - - "docs/**" # Ignore documentation - - "examples/**" # Ignore example code + - "ci/**" # Ignore CI scripts + - "docs/**" # Ignore documentation + - "examples/**" # Ignore example code github_checks: annotations: true diff --git a/form/CMakeLists.txt b/form/CMakeLists.txt index 3bc412e16..60042d19b 100644 --- a/form/CMakeLists.txt +++ b/form/CMakeLists.txt @@ -32,3 +32,8 @@ target_link_libraries(form_module PRIVATE phlex::module form) target_include_directories(form_module PRIVATE ${PROJECT_SOURCE_DIR}) install(TARGETS form_module LIBRARY DESTINATION lib) + +add_library(form_source MODULE form_source.cpp) +target_link_libraries(form_source PRIVATE phlex::module form) +target_include_directories(form_source PRIVATE ${PROJECT_SOURCE_DIR}) +install(TARGETS form_source LIBRARY DESTINATION lib) diff --git a/form/form_source.cpp b/form/form_source.cpp new file mode 100644 index 000000000..280aaa613 --- /dev/null +++ b/form/form_source.cpp @@ -0,0 +1,61 @@ +#include "form/config.hpp" +#include "form/form.hpp" +#include "form/technology.hpp" +#include "phlex/source.hpp" + +#include +#include +#include +#include +#include +#include + +using namespace phlex; +using namespace phlex::experimental; + +PHLEX_REGISTER_PROVIDERS(s, config) +{ + // --- Extract configuration --- + std::string const input_file = config.get("input_file"); + std::string const creator = config.get("creator"); + std::string const tech_string = config.get("technology", "ROOT_TTREE"); + auto const products = config.get>("products"); + + // --- Resolve technology enum --- + std::unordered_map const tech_lookup = { + {"ROOT_TTREE", form::technology::ROOT_TTREE}, + {"ROOT_RNTUPLE", form::technology::ROOT_RNTUPLE}, + {"HDF5", form::technology::HDF5}}; + + auto const it = tech_lookup.find(tech_string); + if (it == tech_lookup.end()) { + throw std::runtime_error("Unknown technology: " + tech_string); + } + int const technology = it->second; + + // --- Build input config --- + form::experimental::config::output_item_config input_cfg; + form::experimental::config::tech_setting_config tech_cfg; + for (auto const& name : products) { + input_cfg.addItem(name, input_file, technology); + } + + // --- Create shared form_interface --- + auto reader = std::make_shared(input_cfg, tech_cfg); + + // --- Register providers dynamically from config --- + // FIXME: Prototype 0.1 -- types hardcoded as int. + for (auto const& name : products) { + s.provide("provide_" + name, + [reader, creator, name](data_cell_index const& id) -> int { + form::experimental::product_with_name pb{name, nullptr, &typeid(int)}; + reader->read(creator, id.to_string(), pb); + if (!pb.data) { + throw std::runtime_error("FORM read returned null data for product: " + name); + } + return *static_cast(pb.data); + }) + .output_product(product_query{ + .creator = identifier(creator), .layer = identifier("event"), .suffix = identifier(name)}); + } +}