diff --git a/.github/actions/build-cmake/action.yaml b/.github/actions/build-cmake/action.yaml index b403b245..bccaa938 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 7f23fd5c..c0a2e717 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 66e6fff4..968d7e86 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 3c2bdd8b..2b759029 100644 --- a/.github/actions/detect-relevant-changes/action.yaml +++ b/.github/actions/detect-relevant-changes/action.yaml @@ -21,7 +21,9 @@ inputs: required: false default: "" type-pattern-add: - description: "Additional type-to-glob mappings (comma or newline separated, e.g. cpp:*.cc). '.in' variants are added automatically." + description: >- + Additional type-to-glob mappings (comma or newline separated, e.g. cpp:*.cc). '.in' variants are added + automatically. required: false default: "" outputs: @@ -44,6 +46,7 @@ runs: FILE_TYPE: ${{ inputs.file-type }} INCLUDE_GLOBS: ${{ inputs.include-globs }} TYPE_PATTERN_ADD: ${{ inputs.type-pattern-add }} + # yamllint disable rule:line-length run: | set -euo pipefail @@ -55,10 +58,12 @@ runs: DEFAULT_TYPE_PATTERNS[python]=$'*.py' DEFAULT_TYPE_PATTERNS[md]=$'*.md' DEFAULT_TYPE_PATTERNS[jsonnet]=$'*.jsonnet\n*.libsonnet' + DEFAULT_TYPE_PATTERNS[yaml]=$'*.yaml\n*.yml' # Types that do not support .in variants for relevance detection declare -A NO_IN_VARIANT_TYPES NO_IN_VARIANT_TYPES[jsonnet]="1" - + NO_IN_VARIANT_TYPES[yaml]="1" + parse_list() { local input="$1" printf '%s' "$input" | tr ',' '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e '/^$/d' @@ -208,3 +213,4 @@ runs: echo "matched=false" >> "$GITHUB_OUTPUT" echo "matched_files=" >> "$GITHUB_OUTPUT" fi + # yamllint enable diff --git a/.github/actions/generate-build-matrix/action.yaml b/.github/actions/generate-build-matrix/action.yaml index 384d2682..39443064 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 e03d52ac..d1788af6 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 eb1d81af..13f9154b 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 @@ -73,7 +73,9 @@ runs: }); core.setOutput('maintainer_can_modify', pr.maintainer_can_modify); - name: Commit fixes - if: steps.check_changes.outputs.changes == 'true' && (inputs.pr-info-repo == github.repository || steps.pr-properties.outputs.maintainer_can_modify == 'true') + if: >- + steps.check_changes.outputs.changes == 'true' && (inputs.pr-info-repo == github.repository || + steps.pr-properties.outputs.maintainer_can_modify == 'true') id: commit_and_push shell: bash working-directory: ${{ inputs.working-directory }} @@ -126,12 +128,16 @@ runs: if: steps.commit_and_push.conclusion == 'success' && steps.commit_and_push.outputs.pushed == 'true' uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 with: + # yamllint disable rule:line-length message: | Automatic ${{ inputs.tool }} fixes pushed (commit ${{ steps.commit_and_push.outputs.commit_sha_short || steps.commit_and_push.outputs.commit_sha }}). ⚠️ **Note:** Some issues may require manual review and fixing. + # yamllint enable - name: Create patch - if: steps.check_changes.outputs.changes == 'true' && inputs.pr-info-repo != github.repository && steps.pr-properties.outputs.maintainer_can_modify != 'true' + if: >- + steps.check_changes.outputs.changes == 'true' && inputs.pr-info-repo != github.repository && + steps.pr-properties.outputs.maintainer_can_modify != 'true' id: create_patch shell: bash working-directory: ${{ inputs.working-directory }} @@ -150,6 +156,7 @@ runs: if: steps.create_patch.outputs.patch_name uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 with: + # yamllint disable rule:line-length message: | Automatic fixes are available but could not be applied directly because this PR is from a fork and "Allow edits from maintainers" is not enabled. Please apply the patch below to fix the issues: @@ -162,3 +169,4 @@ runs: ```bash git commit -am "Apply ${{ inputs.tool }} fixes" ``` + # yamllint enable diff --git a/.github/actions/setup-build-env/action.yaml b/.github/actions/setup-build-env/action.yaml index e53b9c0d..95a870a4 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 31c68dbb..4bb2c38c 100644 --- a/.github/workflows/actionlint-check.yaml +++ b/.github/workflows/actionlint-check.yaml @@ -5,7 +5,7 @@ permissions: contents: read pull-requests: read -on: +"on": pull_request: workflow_dispatch: inputs: @@ -42,16 +42,22 @@ on: type: string env: - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} jobs: pre-check: runs-on: ubuntu-latest outputs: is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || github.sha }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || github.sha }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} + base_sha: + ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || + github.event.before }} steps: - name: Detect act environment id: detect_act @@ -60,12 +66,10 @@ jobs: detect-changes: needs: pre-check if: > - github.event_name != 'workflow_dispatch' && - ( + github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' + ) && needs.pre-check.outputs.is_act != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -73,42 +77,41 @@ 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] if: > - always() && - ( + always() && ( needs.detect-changes.result == 'skipped' || ( needs.detect-changes.result == 'success' && @@ -120,51 +123,48 @@ 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] if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - ( + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' && - (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') + ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && + needs.detect-changes.outputs.has_changes != 'true') runs-on: ubuntu-latest permissions: 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/add-issues.yaml b/.github/workflows/add-issues.yaml index 61447a2c..c8787a9e 100644 --- a/.github/workflows/add-issues.yaml +++ b/.github/workflows/add-issues.yaml @@ -1,6 +1,6 @@ name: Add issues to issues project -on: +"on": issues: types: - opened diff --git a/.github/workflows/clang-format-check.yaml b/.github/workflows/clang-format-check.yaml index 43fbe14d..97d809ee 100644 --- a/.github/workflows/clang-format-check.yaml +++ b/.github/workflows/clang-format-check.yaml @@ -5,7 +5,7 @@ permissions: contents: read pull-requests: read -on: +"on": pull_request: workflow_dispatch: inputs: @@ -30,8 +30,7 @@ jobs: detect-changes: needs: pre-check if: > - github.event_name != 'workflow_dispatch' && - needs.pre-check.outputs.is_act != 'true' + github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -39,38 +38,37 @@ 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] if: > - always() && - ( + always() && ( needs.detect-changes.result == 'skipped' || ( needs.detect-changes.result == 'success' && @@ -82,47 +80,45 @@ 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] if: > - github.event_name != 'workflow_dispatch' && - needs.pre-check.outputs.is_act != 'true' && - needs.detect-changes.result == 'success' && - needs.detect-changes.outputs.has_changes != 'true' + github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act != 'true' && + needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true' runs-on: ubuntu-latest permissions: 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-format-fix.yaml b/.github/workflows/clang-format-fix.yaml index ceeb22b7..e6e04c06 100644 --- a/.github/workflows/clang-format-fix.yaml +++ b/.github/workflows/clang-format-fix.yaml @@ -1,7 +1,7 @@ name: Clang-Format Fix run-name: "${{ github.actor }} fixing C++ code format" -on: +"on": issue_comment: types: - created @@ -21,8 +21,7 @@ jobs: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || - ( + github.event_name == 'workflow_dispatch' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && @@ -33,9 +32,10 @@ jobs: ) # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. # This covers repo owners, invited collaborators, and all org members. - # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: - ref: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} + ref: + ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || + steps.get_pr.outputs.ref }} repo: ${{ steps.get_pr.outputs.repo || github.repository }} steps: @@ -61,7 +61,7 @@ jobs: with: source: "./phlex-src" clangFormatVersion: 20 - inplace: True + inplace: "True" extensions: cpp,hpp,cpp.in,hpp.in - name: Handle fix commit diff --git a/.github/workflows/clang-tidy-check.yaml b/.github/workflows/clang-tidy-check.yaml index 22661c67..c26bfbe4 100644 --- a/.github/workflows/clang-tidy-check.yaml +++ b/.github/workflows/clang-tidy-check.yaml @@ -1,12 +1,11 @@ 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: +"on": pull_request: issue_comment: types: [created] @@ -20,9 +19,7 @@ on: jobs: pre-check: if: > - github.event_name == 'workflow_dispatch' || - github.event_name == 'pull_request' || - ( + github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && @@ -31,7 +28,9 @@ jobs: runs-on: ubuntu-latest outputs: is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.pr.outputs.ref || github.sha }} + ref: + ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || + steps.pr.outputs.ref || github.sha }} repo: ${{ steps.pr.outputs.repo || github.repository }} base_sha: ${{ steps.pr.outputs.base_sha || github.event.pull_request.base.sha || github.event.before }} pr_number: ${{ github.event.pull_request.number || github.event.issue.number }} @@ -48,9 +47,8 @@ jobs: detect-changes: needs: pre-check if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - needs.pre-check.outputs.is_act != 'true' + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act + != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -60,40 +58,39 @@ 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] if: > - always() && - ( + always() && ( needs.detect-changes.result == 'skipped' || ( needs.detect-changes.result == 'success' && @@ -101,81 +98,84 @@ 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: Run clang-tidy using CMake - id: tidy - shell: bash - run: | - . /entrypoint.sh - cd "$GITHUB_WORKSPACE/phlex-build" + - 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 }} - echo "➡️ Running clang-tidy checks..." - cmake_status=0 - cmake --build . -j "$(nproc)" > clang-tidy.log 2>&1 || cmake_status=$? + - name: Setup build environment + uses: Framework-R-D/phlex/.github/actions/setup-build-env@main - # 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: 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: 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] - if: always() && needs.clang-tidy-check.result == 'success' && (github.event_name == 'pull_request' || github.event_name == 'issue_comment') + if: + always() && needs.clang-tidy-check.result == 'success' && (github.event_name == 'pull_request' || + github.event_name == 'issue_comment') runs-on: ubuntu-latest permissions: pull-requests: write @@ -199,7 +199,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' @@ -273,14 +273,12 @@ jobs: clang-tidy-check-skipped: needs: [pre-check, detect-changes] if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - needs.pre-check.outputs.is_act != 'true' && - (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act + != 'true' && (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') runs-on: ubuntu-latest permissions: 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 d77ee5fa..065429e8 100644 --- a/.github/workflows/clang-tidy-fix.yaml +++ b/.github/workflows/clang-tidy-fix.yaml @@ -1,7 +1,7 @@ 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: +"on": issue_comment: types: - created @@ -25,8 +25,7 @@ jobs: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || - ( + github.event_name == 'workflow_dispatch' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && @@ -34,11 +33,14 @@ jobs: ) # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. # This covers repo owners, invited collaborators, and all org members. - # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: - ref: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} + ref: + ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || + steps.get_pr.outputs.ref }} repo: ${{ steps.get_pr.outputs.repo || github.repository }} - tidy_checks: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tidy-checks || steps.parse_comment.outputs.tidy_checks }} + tidy_checks: + ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tidy-checks || + steps.parse_comment.outputs.tidy_checks }} steps: - id: parse_comment @@ -48,7 +50,8 @@ jobs: COMMENT_BODY: ${{ github.event.comment.body }} run: | bot_name="${{ github.event.repository.name }}bot" - checks_line=$(echo "$COMMENT_BODY" | sed -nE "s/^@${bot_name}[[:space:]]+tidy-fix[[:space:]]+(.*)/\1/p" | tr -d '\r') + checks_line=$(echo "$COMMENT_BODY" | + sed -nE "s/^@${bot_name}[[:space:]]+tidy-fix[[:space:]]+(.*)/\1/p" | tr -d '\r') if [ -n "$checks_line" ]; then tidy_checks="$(echo "$checks_line" | tr ',' ' ' | xargs -n1 | paste -sd, -)" echo "tidy_checks=$tidy_checks" >> "$GITHUB_OUTPUT" @@ -100,7 +103,9 @@ jobs: - name: Apply fixes from artifact if available id: apply_from_artifact - if: needs.pre-check.outputs.tidy_checks == '' && (steps.download_fixes_check.outcome == 'success' || steps.download_fixes_fix.outcome == 'success') + if: + needs.pre-check.outputs.tidy_checks == '' && (steps.download_fixes_check.outcome == 'success' || + steps.download_fixes_fix.outcome == 'success') run: | if [ -f fixes/clang-tidy-fixes.yaml ]; then echo "Applying fixes from existing artifact..." @@ -136,7 +141,9 @@ jobs: 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='${{ steps.prep_tidy_opts.outputs.clang_tidy_opts }}'" + extra-options: + "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_SCAN_FOR_MODULES=OFF -DCMAKE_CXX_CLANG_TIDY='${{ + steps.prep_tidy_opts.outputs.clang_tidy_opts }}'" - name: Generate clang-tidy fixes using CMake build if: steps.apply_from_artifact.outputs.applied != 'true' diff --git a/.github/workflows/cmake-build.yaml b/.github/workflows/cmake-build.yaml index 310f79a4..34d7e6bb 100644 --- a/.github/workflows/cmake-build.yaml +++ b/.github/workflows/cmake-build.yaml @@ -1,12 +1,12 @@ name: CMake Build and Test run-name: "${{ github.actor }} building and testing ${{ github.repository }}" -on: +"on": pull_request: 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: @@ -67,17 +67,18 @@ permissions: env: BUILD_TYPE: Release CICOLOR_FORCE: 1 - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} - local_build_path: ${{ (github.event_name == 'workflow_call' && inputs.build-path) || format('{0}-build', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} + local_build_path: + ${{ (github.event_name == 'workflow_call' && inputs.build-path) || format('{0}-build', github.event.repository.name) + }} jobs: pre-check: if: > - github.event_name == 'workflow_dispatch' || - github.event_name == 'pull_request' || - github.event_name == 'push' || - github.event_name == 'workflow_call' || - ( + github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || github.event_name == 'push' || + github.event_name == 'workflow_call' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && @@ -85,7 +86,6 @@ jobs: ) # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. # This covers repo owners, invited collaborators, and all org members. - # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. runs-on: ubuntu-latest permissions: contents: read @@ -93,9 +93,13 @@ jobs: outputs: is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.pr.outputs.ref || steps.pr.outputs.sha || github.sha }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || steps.pr.outputs.ref || steps.pr.outputs.sha || github.sha }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || steps.pr.outputs.repo || github.repository }} - base_sha: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || steps.pr.outputs.base_sha || github.event.pull_request.base.sha || github.event.before }} + base_sha: + ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || steps.pr.outputs.base_sha || + github.event.pull_request.base.sha || github.event.before }} steps: - name: Get PR Info @@ -110,8 +114,7 @@ jobs: detect-changes: needs: pre-check if: > - needs.pre-check.result == 'success' && - ( + needs.pre-check.result == 'success' && ( github.event_name == 'pull_request' || github.event_name == 'push' || ( @@ -120,8 +123,7 @@ jobs: github.event.inputs == null && github.event.comment == null ) - ) && - needs.pre-check.outputs.is_act != 'true' + ) && needs.pre-check.outputs.is_act != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -168,15 +170,15 @@ jobs: - id: generate uses: Framework-R-D/phlex/.github/actions/generate-build-matrix@main with: - user-input: ${{ (github.event_name == 'workflow_call' && inputs.build-combinations) || github.event.inputs.build-combinations }} + user-input: + ${{ (github.event_name == 'workflow_call' && inputs.build-combinations) || + github.event.inputs.build-combinations }} comment-body: ${{ github.event.comment.body }} build: needs: [pre-check, detect-changes, generate-matrix] if: > - always() && - needs.pre-check.result == 'success' && - ( + always() && needs.pre-check.result == 'success' && ( needs.detect-changes.result == 'skipped' || ( needs.detect-changes.result == 'success' && @@ -192,103 +194,98 @@ 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] if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - ( + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' && - (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') + ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && + needs.detect-changes.outputs.has_changes != 'true') runs-on: ubuntu-latest permissions: 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] if: > - always() && - github.event_name == 'issue_comment' && - needs.pre-check.result == 'success' + always() && github.event_name == 'issue_comment' && needs.pre-check.result == 'success' runs-on: ubuntu-latest permissions: pull-requests: write @@ -297,13 +294,15 @@ jobs: - name: Comment on build completion uses: Framework-R-D/phlex/.github/actions/pr-comment@main with: + # yamllint disable rule:line-length 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.' + + **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. + # yamllint enable diff --git a/.github/workflows/cmake-format-check.yaml b/.github/workflows/cmake-format-check.yaml index 09c9b40f..377cf295 100644 --- a/.github/workflows/cmake-format-check.yaml +++ b/.github/workflows/cmake-format-check.yaml @@ -5,7 +5,7 @@ permissions: contents: read pull-requests: read -on: +"on": pull_request: workflow_dispatch: inputs: @@ -42,16 +42,22 @@ on: type: string env: - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} jobs: pre-check: runs-on: ubuntu-latest outputs: is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || github.sha }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || github.sha }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} + base_sha: + ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || + github.event.before }} steps: - name: Detect act environment id: detect_act @@ -60,12 +66,10 @@ jobs: detect-changes: needs: pre-check if: > - github.event_name != 'workflow_dispatch' && - ( + github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' + ) && needs.pre-check.outputs.is_act != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -73,38 +77,37 @@ 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] if: > - always() && - ( + always() && ( needs.detect-changes.result == 'skipped' || ( needs.detect-changes.result == 'success' && @@ -112,54 +115,51 @@ 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' + # yamllint disable rule:line-length + 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 + # yamllint enable cmake-format-check-skipped: needs: [pre-check, detect-changes] if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - ( + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' && - (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') + ) && 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 6aac9e33..cc0871a4 100644 --- a/.github/workflows/cmake-format-fix.yaml +++ b/.github/workflows/cmake-format-fix.yaml @@ -1,7 +1,7 @@ name: CMake Format Fix run-name: "${{ github.actor }} fixing CMake format" -on: +"on": issue_comment: types: - created @@ -31,16 +31,16 @@ permissions: contents: write env: - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} jobs: pre-check: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || - github.event_name == 'workflow_call' || - ( + github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && @@ -51,10 +51,13 @@ jobs: ) # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. # This covers repo owners, invited collaborators, and all org members. - # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && github.repository) || steps.get_pr.outputs.repo }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} + repo: + ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && + github.repository) || steps.get_pr.outputs.repo }} steps: - name: Get PR Info @@ -79,7 +82,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 bcba9a31..0eefe100 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -1,12 +1,12 @@ # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json name: "CodeQL Analysis" -on: +"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: @@ -70,11 +70,17 @@ jobs: runs-on: ubuntu-latest outputs: is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || github.sha }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || github.sha }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} + base_sha: + ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || + github.event.before }} skip_detection: ${{ steps.should_skip.outputs.skip }} - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} steps: - name: Detect act environment id: detect_act @@ -97,8 +103,7 @@ jobs: detect-changes-cpp: needs: pre-check if: > - needs.pre-check.result == 'success' && - needs.pre-check.outputs.skip_detection != 'true' + needs.pre-check.result == 'success' && needs.pre-check.outputs.skip_detection != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -137,8 +142,7 @@ jobs: detect-changes-python: needs: pre-check if: > - needs.pre-check.result == 'success' && - needs.pre-check.outputs.skip_detection != 'true' + needs.pre-check.result == 'success' && needs.pre-check.outputs.skip_detection != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -175,8 +179,7 @@ jobs: detect-changes-actions: needs: pre-check if: > - needs.pre-check.result == 'success' && - needs.pre-check.outputs.skip_detection != 'true' + needs.pre-check.result == 'success' && needs.pre-check.outputs.skip_detection != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -243,13 +246,16 @@ jobs: # Build array based on detection results langs=() - if [ "${{ needs.detect-changes-cpp.result }}" = "success" ] && [ "${{ needs.detect-changes-cpp.outputs.has_changes }}" = "true" ]; then + if [ "${{ needs.detect-changes-cpp.result }}" = "success" ] && + [ "${{ needs.detect-changes-cpp.outputs.has_changes }}" = "true" ]; then langs+=("cpp") fi - if [ "${{ needs.detect-changes-python.result }}" = "success" ] && [ "${{ needs.detect-changes-python.outputs.has_changes }}" = "true" ]; then + if [ "${{ needs.detect-changes-python.result }}" = "success" ] && + [ "${{ needs.detect-changes-python.outputs.has_changes }}" = "true" ]; then langs+=("python") fi - if [ "${{ needs.detect-changes-actions.result }}" = "success" ] && [ "${{ needs.detect-changes-actions.outputs.has_changes }}" = "true" ]; then + if [ "${{ needs.detect-changes-actions.result }}" = "success" ] && + [ "${{ needs.detect-changes-actions.outputs.has_changes }}" = "true" ]; then langs+=("actions") fi @@ -271,16 +277,19 @@ jobs: codeql: needs: [pre-check, determine-languages] if: > - needs.determine-languages.result == 'success' && - needs.determine-languages.outputs.languages != '[]' + needs.determine-languages.result == 'success' && needs.determine-languages.outputs.languages != '[]' name: Analyze ${{ matrix.language }} with CodeQL runs-on: ubuntu-24.04 container: image: ghcr.io/framework-r-d/phlex-ci:latest env: local_checkout_path: ${{ needs.pre-check.outputs.local_checkout_path }} - local_build_path: ${{ (github.event_name == 'workflow_call' && inputs.build-path) || format('{0}-build', github.event.repository.name) }} - CODEQL_EXTRACTOR_CPP_COMPILATION_DATABASE: ${{ github.workspace }}/${{ (github.event_name == 'workflow_call' && inputs.build-path) || format('{0}-build', github.event.repository.name) }}/compile_commands.json + local_build_path: + ${{ (github.event_name == 'workflow_call' && inputs.build-path) || format('{0}-build', + github.event.repository.name) }} + CODEQL_EXTRACTOR_CPP_COMPILATION_DATABASE: + ${{ github.workspace }}/${{ (github.event_name == 'workflow_call' && inputs.build-path) || format('{0}-build', + github.event.repository.name) }}/compile_commands.json strategy: fail-fast: false matrix: @@ -412,8 +421,7 @@ jobs: - name: Prepare PR comment data if: >- - github.event_name == 'pull_request' && - steps.check_codeql.outputs.comment_path != '' + github.event_name == 'pull_request' && steps.check_codeql.outputs.comment_path != '' run: | mkdir -p pr_comment_data echo "${{ github.event.pull_request.number }}" > pr_comment_data/pr_number.txt @@ -421,8 +429,7 @@ jobs: - name: Upload PR comment data if: >- - github.event_name == 'pull_request' && - steps.check_codeql.outputs.comment_path != '' + github.event_name == 'pull_request' && steps.check_codeql.outputs.comment_path != '' uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: codeql-pr-data @@ -432,8 +439,7 @@ jobs: - name: Fail workflow due to new CodeQL alerts # Fails the check on the PR so the user sees red X if: >- - github.event_name == 'pull_request' && - steps.check_codeql.outputs.new_alerts == 'true' + github.event_name == 'pull_request' && steps.check_codeql.outputs.new_alerts == 'true' run: | echo "New CodeQL alerts detected; failing job." exit 1 diff --git a/.github/workflows/codeql-comment.yaml b/.github/workflows/codeql-comment.yaml index 89d1e97f..9cb9c319 100644 --- a/.github/workflows/codeql-comment.yaml +++ b/.github/workflows/codeql-comment.yaml @@ -1,6 +1,6 @@ name: "CodeQL - Post Comment" -on: +"on": workflow_run: workflows: ["CodeQL Analysis"] types: @@ -19,7 +19,9 @@ jobs: # in both cases (new alerts found, or existing alerts resolved). # The workflow is resilient to other failures because the 'Post Comment' step # only runs if the artifact was successfully downloaded. - if: github.event.workflow_run.event == 'pull_request' && (github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure') + if: + github.event.workflow_run.event == 'pull_request' && (github.event.workflow_run.conclusion == 'success' || + github.event.workflow_run.conclusion == 'failure') steps: - name: Download PR comment data uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index e879a77a..1aa6bb33 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -2,9 +2,9 @@ name: Code Coverage run-name: "${{ github.actor }} running code coverage" -on: +"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 @@ -30,9 +30,7 @@ permissions: jobs: pre-check: if: > - github.event_name == 'workflow_dispatch' || - github.event_name == 'pull_request' || - github.event_name == 'push' || + github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || github.event_name == 'push' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && @@ -41,11 +39,12 @@ jobs: ) # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. # This covers repo owners, invited collaborators, and all org members. - # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. runs-on: ubuntu-latest outputs: is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.pr.outputs.ref || github.sha }} + ref: + ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || + steps.pr.outputs.ref || github.sha }} repo: ${{ steps.pr.outputs.repo || github.repository }} base_sha: ${{ steps.pr.outputs.base_sha || github.event.pull_request.base.sha || github.event.before }} steps: @@ -60,10 +59,8 @@ jobs: detect-changes: needs: pre-check if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - github.event_name != 'issue_comment' && - needs.pre-check.outputs.is_act != 'true' + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && github.event_name != + 'issue_comment' && needs.pre-check.outputs.is_act != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -71,41 +68,40 @@ 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] if: > - always() && - ( + always() && ( needs.detect-changes.result == 'skipped' || ( needs.detect-changes.result == 'success' && @@ -113,7 +109,6 @@ jobs: ) ) runs-on: ubuntu-24.04 - container: image: ghcr.io/framework-r-d/phlex-ci:latest @@ -127,317 +122,326 @@ 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." + - name: Determine coverage options + id: coverage_options + shell: bash + # yamllint disable rule:line-length + 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" + # yamllint enable + + - 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 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." - 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 "::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 "✅ Python coverage report generation succeeded." + 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 + 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 "::warning::Python coverage report generation failed (non-fatal)." + 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 - 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: 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 + # 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 - 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 + # 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' }} + 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@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] if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - needs.pre-check.outputs.is_act != 'true' && - (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act + != 'true' && (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') runs-on: ubuntu-latest permissions: 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/dependabot-auto-merge.yaml b/.github/workflows/dependabot-auto-merge.yaml index c3781f74..b55bcdfe 100644 --- a/.github/workflows/dependabot-auto-merge.yaml +++ b/.github/workflows/dependabot-auto-merge.yaml @@ -1,5 +1,5 @@ name: Dependabot auto-merge -on: +"on": # Use pull_request_target instead of pull_request to get elevated permissions # This is safe for Dependabot PRs because: # 1. We verify the PR author is dependabot[bot] @@ -19,10 +19,16 @@ permissions: jobs: dependabot: runs-on: ubuntu-latest - if: | - (github.event_name == 'pull_request_target' && github.event.pull_request.user.login == 'dependabot[bot]' && github.event.pull_request.base.ref == 'main') || - (github.event_name == 'pull_request_review' && github.event.pull_request.user.login == 'dependabot[bot]' && github.event.pull_request.base.ref == 'main') || - (github.event_name == 'check_suite' && github.event.check_suite.pull_requests[0] != null && startsWith(github.event.check_suite.head_branch, 'dependabot/')) + if: >- + (github.event_name == 'pull_request_target' && + github.event.pull_request.user.login == 'dependabot[bot]' && + github.event.pull_request.base.ref == 'main') || + (github.event_name == 'pull_request_review' && + github.event.pull_request.user.login == 'dependabot[bot]' && + github.event.pull_request.base.ref == 'main') || + (github.event_name == 'check_suite' && + github.event.check_suite.pull_requests[0] != null && + startsWith(github.event.check_suite.head_branch, 'dependabot/')) steps: - name: Get PR details id: pr @@ -36,7 +42,7 @@ jobs: PR_JSON=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json author,baseRefName) AUTHOR=$(echo "$PR_JSON" | jq -r '.author.login') BASE_REF=$(echo "$PR_JSON" | jq -r '.baseRefName') - + { echo "author=$AUTHOR" echo "base_ref=$BASE_REF" @@ -56,6 +62,7 @@ jobs: - name: Enable auto-merge for Dependabot PRs if: steps.pr.outputs.author == 'dependabot[bot]' && steps.pr.outputs.base_ref == 'main' shell: bash + # yamllint disable rule:line-length run: | set -o pipefail if ! gh pr merge --auto --merge "${{ steps.pr.outputs.number }}" --repo "${{ github.repository }}" 2>&1 | tee /tmp/gh-output.txt; then @@ -68,5 +75,7 @@ jobs: exit 1 fi fi + # yamllint enable + env: GH_TOKEN: ${{ secrets.WORKFLOW_PAT }} diff --git a/.github/workflows/header-guards-check.yaml b/.github/workflows/header-guards-check.yaml index 995c1755..3e163a01 100644 --- a/.github/workflows/header-guards-check.yaml +++ b/.github/workflows/header-guards-check.yaml @@ -5,7 +5,7 @@ permissions: contents: read pull-requests: read -on: +"on": pull_request: workflow_dispatch: inputs: @@ -42,16 +42,22 @@ on: type: string env: - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} jobs: pre-check: runs-on: ubuntu-latest outputs: is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || github.sha }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || github.sha }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} + base_sha: + ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || + github.event.before }} steps: - name: Detect act environment id: detect_act @@ -60,13 +66,10 @@ jobs: detect-changes: needs: pre-check if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - ( + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' + ) && needs.pre-check.outputs.is_act != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -74,38 +77,37 @@ 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] if: > - always() && - ( + always() && ( needs.detect-changes.result == 'skipped' || ( needs.detect-changes.result == 'success' && @@ -117,47 +119,45 @@ 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] if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - ( + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' && - (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') + ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && + needs.detect-changes.outputs.has_changes != 'true') runs-on: ubuntu-latest permissions: 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 25254ce3..82b776b9 100644 --- a/.github/workflows/header-guards-fix.yaml +++ b/.github/workflows/header-guards-fix.yaml @@ -1,7 +1,7 @@ name: Header Guards Fix run-name: "${{ github.actor }} fixing header guards" -on: +"on": issue_comment: types: - created @@ -31,16 +31,16 @@ permissions: contents: write env: - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} jobs: pre-check: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || - github.event_name == 'workflow_call' || - ( + github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && @@ -51,10 +51,13 @@ jobs: ) # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. # This covers repo owners, invited collaborators, and all org members. - # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && github.repository) || steps.get_pr.outputs.repo }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} + repo: + ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && + github.repository) || steps.get_pr.outputs.repo }} steps: - name: Get PR Info @@ -79,7 +82,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 c2302092..91f7c95d 100644 --- a/.github/workflows/jsonnet-format-check.yaml +++ b/.github/workflows/jsonnet-format-check.yaml @@ -5,7 +5,7 @@ permissions: contents: read pull-requests: read -on: +"on": pull_request: workflow_dispatch: inputs: @@ -42,16 +42,22 @@ on: type: string env: - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} jobs: pre-check: runs-on: ubuntu-latest outputs: is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || github.sha }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || github.sha }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} + base_sha: + ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || + github.event.before }} steps: - name: Detect act environment id: detect_act @@ -60,13 +66,10 @@ jobs: detect-changes: needs: pre-check if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - ( + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' + ) && needs.pre-check.outputs.is_act != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -74,38 +77,37 @@ 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] if: > - always() && - ( + always() && ( needs.detect-changes.result == 'skipped' || ( needs.detect-changes.result == 'success' && @@ -117,45 +119,43 @@ 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' + # yamllint disable rule:line-length + 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 + # yamllint enable jsonnet-format-check-skipped: needs: [pre-check, detect-changes] if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - ( + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' && - (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') + ) && 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/jsonnet-format-fix.yaml b/.github/workflows/jsonnet-format-fix.yaml index f44945e4..bd3b8983 100644 --- a/.github/workflows/jsonnet-format-fix.yaml +++ b/.github/workflows/jsonnet-format-fix.yaml @@ -1,7 +1,7 @@ name: Jsonnet Format Fix run-name: "${{ github.actor }} fixing Jsonnet format" -on: +"on": issue_comment: types: - created @@ -31,16 +31,16 @@ permissions: contents: write env: - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} jobs: pre-check: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || - github.event_name == 'workflow_call' || - ( + github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && @@ -51,10 +51,13 @@ jobs: ) # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. # This covers repo owners, invited collaborators, and all org members. - # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && github.repository) || steps.get_pr.outputs.repo }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} + repo: + ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && + github.repository) || steps.get_pr.outputs.repo }} steps: - name: Get PR Info @@ -81,9 +84,11 @@ jobs: id: lint env: CHECKOUT_PATH: ${{ env.local_checkout_path }} + # yamllint disable rule:line-length run: | docker run --rm -v "${{ github.workspace }}:/work" -w /work --user root public.ecr.aws/bitnami/jsonnet:latest \ sh -c "find \"$CHECKOUT_PATH\" \( -name '*.jsonnet' -o -name '*.libsonnet' \) -print0 | xargs -0 -r jsonnetfmt -i" + # yamllint enable continue-on-error: true - name: Handle fix commit diff --git a/.github/workflows/markdown-check.yaml b/.github/workflows/markdown-check.yaml index 1c340faa..e2b04050 100644 --- a/.github/workflows/markdown-check.yaml +++ b/.github/workflows/markdown-check.yaml @@ -5,7 +5,7 @@ permissions: contents: read pull-requests: read -on: +"on": pull_request: workflow_dispatch: inputs: @@ -42,16 +42,22 @@ on: type: string env: - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} jobs: pre-check: runs-on: ubuntu-latest outputs: is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || github.sha }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || github.sha }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} + base_sha: + ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || + github.event.before }} steps: - name: Detect act environment id: detect_act @@ -60,13 +66,10 @@ jobs: detect-changes: needs: pre-check if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - ( + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' + ) && needs.pre-check.outputs.is_act != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -74,38 +77,37 @@ 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] if: > - always() && - ( + always() && ( needs.detect-changes.result == 'skipped' || ( needs.detect-changes.result == 'success' && @@ -117,51 +119,50 @@ 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' + # yamllint disable rule:line-length + 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 + # yamllint enable markdown-check-skipped: needs: [pre-check, detect-changes] if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - ( + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' && - (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') + ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && + needs.detect-changes.outputs.has_changes != 'true') runs-on: ubuntu-latest permissions: 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 9261e2f6..fa30c014 100644 --- a/.github/workflows/markdown-fix.yaml +++ b/.github/workflows/markdown-fix.yaml @@ -1,7 +1,7 @@ name: Markdown Fix run-name: "${{ github.actor }} fixing Markdown format" -on: +"on": issue_comment: types: - created @@ -31,16 +31,16 @@ permissions: contents: write env: - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} jobs: pre-check: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || - github.event_name == 'workflow_call' || - ( + github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && @@ -51,10 +51,13 @@ jobs: ) # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. # This covers repo owners, invited collaborators, and all org members. - # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && github.repository) || steps.get_pr.outputs.repo }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} + repo: + ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && + github.repository) || steps.get_pr.outputs.repo }} steps: - name: Get PR Info @@ -79,7 +82,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 daecf149..8724da18 100644 --- a/.github/workflows/python-check.yaml +++ b/.github/workflows/python-check.yaml @@ -5,7 +5,7 @@ permissions: contents: read pull-requests: read -on: +"on": pull_request: workflow_dispatch: inputs: @@ -42,16 +42,22 @@ on: type: string env: - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} jobs: pre-check: runs-on: ubuntu-latest outputs: is_act: ${{ steps.detect_act.outputs.is_act }} - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || github.sha }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || github.sha }} repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || github.repository }} - base_sha: ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || github.event.before }} + base_sha: + ${{ (github.event_name == 'workflow_call' && inputs.pr-base-sha) || github.event.pull_request.base.sha || + github.event.before }} steps: - name: Detect act environment id: detect_act @@ -60,13 +66,10 @@ jobs: detect-changes: needs: pre-check if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - ( + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' + ) && needs.pre-check.outputs.is_act != 'true' runs-on: ubuntu-latest permissions: contents: read @@ -74,38 +77,37 @@ 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] if: > - always() && - ( + always() && ( needs.detect-changes.result == 'skipped' || ( needs.detect-changes.result == 'success' && @@ -117,65 +119,64 @@ 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') + # yamllint disable rule:line-length + 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 + # yamllint enable python-check-skipped: needs: [pre-check, detect-changes] if: > - needs.pre-check.result == 'success' && - github.event_name != 'workflow_dispatch' && - ( + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && ( github.event_name != 'workflow_call' || (inputs.skip-relevance-check != 'true' && github.event.inputs == null && github.event.comment == null) - ) && - needs.pre-check.outputs.is_act != 'true' && - (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') + ) && needs.pre-check.outputs.is_act != 'true' && (needs.detect-changes.result == 'success' && + needs.detect-changes.outputs.has_changes != 'true') runs-on: ubuntu-latest permissions: 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 0543afe4..5927d926 100644 --- a/.github/workflows/python-fix.yaml +++ b/.github/workflows/python-fix.yaml @@ -1,7 +1,7 @@ name: Python Fix run-name: "${{ github.actor }} fixing Python code" -on: +"on": issue_comment: types: - created @@ -31,16 +31,16 @@ permissions: contents: write env: - local_checkout_path: ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', github.event.repository.name) }} + local_checkout_path: + ${{ (github.event_name == 'workflow_call' && inputs.checkout-path) || format('{0}-src', + github.event.repository.name) }} jobs: pre-check: runs-on: ubuntu-latest name: Parse command if: > - github.event_name == 'workflow_dispatch' || - github.event_name == 'workflow_call' || - ( + github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || ( github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) && @@ -48,10 +48,13 @@ jobs: ) # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. # This covers repo owners, invited collaborators, and all org members. - # See .github/AUTHORIZATION_ANALYSIS.md for security rationale. outputs: - ref: ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} - repo: ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && github.repository) || steps.get_pr.outputs.repo }} + ref: + ${{ (github.event_name == 'workflow_call' && inputs.ref) || (github.event_name == 'workflow_dispatch' && + (github.event.inputs.ref || github.ref)) || steps.get_pr.outputs.ref }} + repo: + ${{ (github.event_name == 'workflow_call' && inputs.repo) || (github.event_name == 'workflow_dispatch' && + github.repository) || steps.get_pr.outputs.repo }} steps: - name: Get PR Info if: github.event_name == 'issue_comment' @@ -74,7 +77,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/.github/workflows/yaml-check.yaml b/.github/workflows/yaml-check.yaml new file mode 100644 index 00000000..1fa6129d --- /dev/null +++ b/.github/workflows/yaml-check.yaml @@ -0,0 +1,123 @@ +name: YAML Check +"run-name": "${{ github.actor }} checking YAML files" + +permissions: + contents: read + +"on": + pull_request: + workflow_dispatch: + inputs: + ref: + description: "The branch, ref, or SHA to checkout. Defaults to the repository's default branch." + required: false + type: string + +jobs: + pre-check: + runs-on: ubuntu-latest + outputs: + is_act: ${{ steps.detect_act.outputs.is_act }} + ref: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || github.sha }} + repo: ${{ github.repository }} + base_sha: ${{ github.event.pull_request.base.sha || github.event.before }} + steps: + - name: Detect act environment + id: detect_act + uses: Framework-R-D/phlex/.github/actions/detect-act-env@main + + detect-changes: + needs: pre-check + if: > + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act + != 'true' + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + 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: 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: yaml + + - name: Report detection outcome + run: | + if [ "${{ steps.filter.outputs.matched }}" != "true" ]; then + echo "::notice::No YAML-related changes detected; check will be skipped." + else + echo "::group::YAML-related files" + printf '%s\n' "${{ steps.filter.outputs.matched_files }}" + echo "::endgroup::" + fi + + yaml-check: + needs: [pre-check, detect-changes] + if: > + always() && ( + needs.detect-changes.result == 'skipped' || + ( + needs.detect-changes.result == 'success' && + needs.detect-changes.outputs.has_changes == 'true' + ) + ) + runs-on: ubuntu-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 Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.x" + + - name: Install yamllint + run: pip install yamllint + + - name: Run yamllint + id: lint + working-directory: phlex-src + run: yamllint . + continue-on-error: true + + - name: Evaluate yamllint result + if: always() && steps.lint.outcome != 'skipped' + run: | + if [ "${{ steps.lint.outcome }}" = "success" ]; then + echo "✅ YAML check passed." + else + echo "::error::YAML check failed." + echo "::error::Comment '@${{ github.event.repository.name }}bot yaml-fix' on the PR to auto-fix." + exit 1 + fi + + yaml-check-skipped: + needs: [pre-check, detect-changes] + if: > + needs.pre-check.result == 'success' && github.event_name != 'workflow_dispatch' && needs.pre-check.outputs.is_act + != 'true' && (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.has_changes != 'true') + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: No relevant YAML changes detected + run: echo "::notice::No YAML-related changes detected; yaml-check skipped." diff --git a/.github/workflows/yaml-fix.yaml b/.github/workflows/yaml-fix.yaml new file mode 100644 index 00000000..d5016a42 --- /dev/null +++ b/.github/workflows/yaml-fix.yaml @@ -0,0 +1,82 @@ +name: YAML Fix +"run-name": "${{ github.actor }} fixing YAML files" + +"on": + 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 + +permissions: + pull-requests: write + contents: write + +jobs: + pre-check: + runs-on: ubuntu-latest + name: Parse command + if: > + github.event_name == 'workflow_dispatch' || ( + 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 format', github.event.repository.name)) || + startsWith(github.event.comment.body, format('@{0}bot yaml-fix', github.event.repository.name)) + ) + ) + # Authorization: Only OWNER, COLLABORATOR, or MEMBER can trigger via comments. + # This covers repo owners, invited collaborators, and all org members. + outputs: + ref: + ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.ref || github.ref)) || + steps.get_pr.outputs.ref }} + repo: ${{ steps.get_pr.outputs.repo || github.repository }} + + steps: + - name: Get PR Info + if: github.event_name == 'issue_comment' + id: get_pr + uses: Framework-R-D/phlex/.github/actions/get-pr-info@main + + apply_yaml_fixes: + runs-on: ubuntu-latest + name: Apply YAML fixes + needs: pre-check + if: ${{ needs.pre-check.result == 'success' }} + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + path: phlex-src + ref: ${{ needs.pre-check.outputs.ref }} + repository: ${{ needs.pre-check.outputs.repo }} + token: ${{ secrets.WORKFLOW_PAT }} + + - name: Setup Node.js + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + with: + node-version: "20.x" + + - name: Install prettier + run: npm install -g prettier + + - name: Apply YAML formatting + run: | + cd phlex-src + prettier --write '**/*.{yaml,yml}' + + - name: Handle fix commit + uses: Framework-R-D/phlex/.github/actions/handle-fix-commit@main + with: + tool: YAML formatter + working-directory: phlex-src + token: ${{ secrets.WORKFLOW_PAT }} + pr-info-ref: ${{ needs.pre-check.outputs.ref }} + pr-info-repo: ${{ needs.pre-check.outputs.repo }} diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 00000000..b0931068 --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,7 @@ +--- +printWidth: 120 +tabWidth: 2 +useTabs: false +proseWrap: always +singleQuote: false +endOfLine: lf diff --git a/.yamllint b/.yamllint new file mode 100644 index 00000000..4b0df617 --- /dev/null +++ b/.yamllint @@ -0,0 +1,15 @@ +--- +extends: default + +rules: + line-length: + max: 120 + level: warning + indentation: + spaces: 2 + comments: + min-spaces-from-content: 1 + document-start: disable + truthy: + allowed-values: ['true', 'false'] + trailing-spaces: enable diff --git a/ci/spack.yaml b/ci/spack.yaml index c894bbb5..e14f367c 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 4c08413c..a21c0530 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,5 +1,5 @@ codecov: - require_ci_to_pass: yes + require_ci_to_pass: true coverage: precision: 2 @@ -21,19 +21,19 @@ comment: layout: "header, diff, flags, files, footer" behavior: default require_changes: false - require_base: no - require_head: yes + require_base: false + require_head: true 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