diff --git a/.github/actions/build-cmake/action.yaml b/.github/actions/build-cmake/action.yaml index bccaa938..314e00a6 100644 --- a/.github/actions/build-cmake/action.yaml +++ b/.github/actions/build-cmake/action.yaml @@ -23,10 +23,10 @@ runs: using: "composite" steps: - shell: bash + working-directory: ${{ inputs.build-path }} run: | # Source the container entrypoint script . /entrypoint.sh - cd "$GITHUB_WORKSPACE/$BUILD_PATH" # Determine parallel jobs if [ -n "$PARALLEL_JOBS" ]; then @@ -47,8 +47,6 @@ runs: # Build cmake --build . $jobs_arg $target_arg env: - SOURCE_PATH: ${{ inputs.source-path }} - BUILD_PATH: ${{ inputs.build-path }} TARGET: ${{ inputs.target }} PARALLEL_JOBS: ${{ inputs.parallel-jobs }} CICOLOR_FORCE: 1 diff --git a/.github/actions/configure-cmake/action.yaml b/.github/actions/configure-cmake/action.yaml index c0a2e717..42ce7b3a 100644 --- a/.github/actions/configure-cmake/action.yaml +++ b/.github/actions/configure-cmake/action.yaml @@ -43,11 +43,11 @@ runs: using: "composite" steps: - shell: bash + working-directory: ${{ inputs.build-path }} run: | # Source the container entrypoint script . /entrypoint.sh - cd "$GITHUB_WORKSPACE/$BUILD_PATH" SOURCE_DIR="$GITHUB_WORKSPACE/$SOURCE_PATH" echo "Configuring with CMake preset: $PRESET" @@ -80,7 +80,6 @@ runs: echo "C++ compiler: $CPP_COMPILER" env: SOURCE_PATH: ${{ inputs.source-path }} - BUILD_PATH: ${{ inputs.build-path }} EXTRA_OPTIONS: ${{ inputs.extra-options }} ENABLE_FORM: ${{ inputs.enable-form }} FORM_ROOT_STORAGE: ${{ inputs.form-root-storage }} diff --git a/.github/actions/generate-build-matrix/generate_matrix.py b/.github/actions/generate-build-matrix/generate_matrix.py index a7d7e9f7..eb44d4cc 100644 --- a/.github/actions/generate-build-matrix/generate_matrix.py +++ b/.github/actions/generate-build-matrix/generate_matrix.py @@ -7,12 +7,10 @@ def get_default_combinations(event_name, all_combinations): """Gets the default build combinations based on the GitHub event type.""" - if event_name in ("push", "pull_request", "pull_request_target"): + if event_name in ("push", "pull_request", "pull_request_target", "workflow_dispatch"): return ["gcc/none"] elif event_name == "issue_comment": return ["gcc/none", "clang/none"] - elif event_name == "workflow_dispatch": - return all_combinations else: # Default to a minimal safe configuration for unknown events return ["gcc/none"] diff --git a/.github/actions/run-change-detection/action.yaml b/.github/actions/run-change-detection/action.yaml index 4a3278f7..0c5d23fc 100644 --- a/.github/actions/run-change-detection/action.yaml +++ b/.github/actions/run-change-detection/action.yaml @@ -42,6 +42,12 @@ runs: with: fetch-depth: 0 path: ${{ inputs.checkout-path }} + # This action is never called from a pull_request_target workflow; all callers + # use pull_request, issue_comment, workflow_dispatch, or workflow_call triggers. + # Even if it were called from pull_request_target, the empty sparse checkout + # below ensures no files from the ref are materialized on disk — only git + # objects are fetched — so no code from the PR is ever executed. + # codeql[actions/pull-request-target-injection] ref: ${{ inputs.ref }} repository: ${{ inputs.repo }} persist-credentials: false diff --git a/.github/workflows/clang-tidy-fix.yaml b/.github/workflows/clang-tidy-fix.yaml index c35e5273..cffa27a8 100644 --- a/.github/workflows/clang-tidy-fix.yaml +++ b/.github/workflows/clang-tidy-fix.yaml @@ -111,19 +111,17 @@ jobs: if: needs.setup.outputs.tidy_checks == '' && (steps.download_fixes_check.outcome == 'success' || steps.download_fixes_fix.outcome == 'success') - env: - CHECKOUT_PATH: ${{ needs.setup.outputs.checkout_path }} + working-directory: ${{ needs.setup.outputs.checkout_path }} run: | # The artifact preserves the build directory prefix in its path structure. FIXES_FILE="" - if [ -d fixes ]; then - FIXES_FILE=$(find fixes -name "clang-tidy-fixes.yaml" | head -1) + if [ -d "$GITHUB_WORKSPACE/fixes" ]; then + FIXES_FILE=$(find "$GITHUB_WORKSPACE/fixes" -name "clang-tidy-fixes.yaml" | head -1) fi if [ -n "$FIXES_FILE" ]; then echo "Applying fixes from existing artifact..." . /entrypoint.sh FIXES_DIR="$(realpath "${FIXES_FILE%/*}")" - cd "${CHECKOUT_PATH}" clang-apply-replacements "${FIXES_DIR}" || true echo "applied=true" >> "$GITHUB_OUTPUT" else @@ -143,7 +141,6 @@ jobs: TIDY_CHECKS: ${{ needs.setup.outputs.tidy_checks }} run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE" CLANG_TIDY_OPTS="clang-tidy;--export-fixes=clang-tidy-fixes.yaml" if [ -n "$TIDY_CHECKS" ]; then @@ -165,20 +162,16 @@ jobs: - name: Generate clang-tidy fixes using CMake build if: steps.apply_from_artifact.outputs.applied != 'true' - env: - BUILD_PATH: ${{ needs.setup.outputs.build_path }} + working-directory: ${{ needs.setup.outputs.build_path }} run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${BUILD_PATH}" cmake --build . -j "$(nproc)" || true - name: Apply clang-tidy fixes if: steps.apply_from_artifact.outputs.applied != 'true' - env: - BUILD_PATH: ${{ needs.setup.outputs.build_path }} + working-directory: ${{ needs.setup.outputs.build_path }} run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${BUILD_PATH}" if [ -f clang-tidy-fixes.yaml ]; then clang-apply-replacements . || true fi diff --git a/.github/workflows/cmake-build.yaml b/.github/workflows/cmake-build.yaml index 3b2535f3..d68ea35e 100644 --- a/.github/workflows/cmake-build.yaml +++ b/.github/workflows/cmake-build.yaml @@ -21,7 +21,7 @@ run-name: "${{ github.actor }} building and testing ${{ github.repository }}" - `all` (run all combinations) - `all -clang/none -clang/valgrind` (run all except specified) - `+clang/none +clang/valgrind` (run default matrix plus specified) - Default (if empty): Run all except clang/none and clang/valgrind. + Default (if empty): Run `gcc/none` required: false default: "" workflow_call: @@ -109,7 +109,12 @@ jobs: generate-matrix: needs: setup - if: needs.setup.result == 'success' + if: > + needs.setup.result == 'success' && ( + github.event_name == 'workflow_dispatch' || + inputs.skip-relevance-check || + needs.setup.outputs.has_changes == 'true' + ) runs-on: ubuntu-latest outputs: matrix: ${{ steps.generate.outputs.matrix }} @@ -179,11 +184,9 @@ jobs: - name: Run tests if: matrix.sanitizer != 'valgrind' - env: - BUILD_PATH: ${{ needs.setup.outputs.build_path }} + working-directory: ${{ needs.setup.outputs.build_path }} run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${BUILD_PATH}" echo "➡️ Running tests..." echo "::group::Running ctest" @@ -198,11 +201,9 @@ jobs: - name: Run Valgrind tests if: matrix.sanitizer == 'valgrind' - env: - BUILD_PATH: ${{ needs.setup.outputs.build_path }} + working-directory: ${{ needs.setup.outputs.build_path }} run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${BUILD_PATH}" echo "➡️ Running Valgrind tests..." echo "::group::Running ctest -T memcheck" @@ -214,6 +215,20 @@ jobs: echo "⚠️ Valgrind tests failed, but the workflow will continue." fi + cmake-build-skipped: + needs: [setup] + if: > + needs.setup.result == 'success' && github.event_name != 'workflow_dispatch' && + !inputs.skip-relevance-check && needs.setup.outputs.is_act != 'true' && + needs.setup.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." + build-complete: needs: [setup, build] if: > @@ -233,7 +248,7 @@ jobs: **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.' + || needs.build.result == 'skipped' && 'ℹ️ No relevant C++ or CMake changes detected; build skipped.' || format('ℹ️ Build job completed with status: {0}.', needs.build.result) }} See the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for detailed results. diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 0bc751dd..fafa41d3 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -162,14 +162,12 @@ jobs: build-path: ${{ needs.setup.outputs.build_path }} - name: Run tests with coverage - env: - BUILD_PATH: ${{ needs.setup.outputs.build_path }} + working-directory: ${{ needs.setup.outputs.build_path }} run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${BUILD_PATH}" echo "➡️ Running tests with coverage..." - PROFILE_ROOT="$GITHUB_WORKSPACE/${BUILD_PATH}/test/profraw" + PROFILE_ROOT="$(pwd)/test/profraw" echo "Cleaning LLVM profile directory: $PROFILE_ROOT" rm -rf "$PROFILE_ROOT" mkdir -p "$PROFILE_ROOT" @@ -189,11 +187,9 @@ jobs: id: report_gcc if: ${{ steps.coverage_options.outputs.compiler == 'gcc' }} shell: bash - env: - BUILD_PATH: ${{ needs.setup.outputs.build_path }} + working-directory: ${{ needs.setup.outputs.build_path }} run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${BUILD_PATH}" echo "➡️ Generating coverage reports for GCC..." echo "::group::Running coverage-gcov target" @@ -210,11 +206,9 @@ jobs: id: report_clang if: ${{ steps.coverage_options.outputs.compiler == 'clang' }} shell: bash - env: - BUILD_PATH: ${{ needs.setup.outputs.build_path }} + working-directory: ${{ needs.setup.outputs.build_path }} run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${BUILD_PATH}" echo "➡️ Generating coverage reports for Clang..." echo "::group::Running coverage-llvm target" @@ -231,11 +225,9 @@ jobs: id: report_python if: ${{ steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped' }} shell: bash - env: - BUILD_PATH: ${{ needs.setup.outputs.build_path }} + working-directory: ${{ needs.setup.outputs.build_path }} run: | . /entrypoint.sh - cd "$GITHUB_WORKSPACE/${BUILD_PATH}" echo "➡️ Generating Python coverage report..." echo "::group::Running coverage-python target" @@ -258,10 +250,8 @@ jobs: id: coverage_outputs if: ${{ steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped' }} shell: bash - env: - BUILD_PATH: ${{ needs.setup.outputs.build_path }} + working-directory: ${{ needs.setup.outputs.build_path }} run: | - cd "$GITHUB_WORKSPACE/${BUILD_PATH}" if [ -f coverage.xml ]; then echo "has_coverage_xml=true" >> "$GITHUB_OUTPUT" else @@ -292,14 +282,12 @@ jobs: - name: Prepare coverage artifact bundle if: ${{ steps.report_gcc.outcome != 'skipped' || steps.report_clang.outcome != 'skipped' }} shell: bash - env: - BUILD_PATH: ${{ needs.setup.outputs.build_path }} + working-directory: ${{ needs.setup.outputs.build_path }} run: | set -euo pipefail ARTIFACT_DIR="$GITHUB_WORKSPACE/coverage-artifacts" rm -rf "$ARTIFACT_DIR" mkdir -p "$ARTIFACT_DIR" - cd "$GITHUB_WORKSPACE/${BUILD_PATH}" for file in \ coverage-llvm.txt \ coverage-llvm.info \ diff --git a/.github/workflows/dependabot-auto-merge.yaml b/.github/workflows/dependabot-auto-merge.yaml index 61ff9d02..adcb4125 100644 --- a/.github/workflows/dependabot-auto-merge.yaml +++ b/.github/workflows/dependabot-auto-merge.yaml @@ -67,14 +67,53 @@ jobs: # yamllint disable rule:line-length run: | set -o pipefail - if ! gh pr merge --auto --rebase "${PR_NUMBER}" --repo "$GITHUB_REPOSITORY" 2>&1 | tee /tmp/gh-output.txt; then - if grep -qE "auto-merge is already enabled|[Rr]equired.*status.*check|[Rr]equired approving review|[Rr]equired.*review" /tmp/gh-output.txt; then - echo "Auto-merge not enabled yet - this is expected when requirements are not met or already enabled" - exit 0 - else - echo "Unexpected error enabling auto-merge:" + + attempt_merge() { + gh pr merge --auto --rebase "${PR_NUMBER}" --repo "$GITHUB_REPOSITORY" 2>&1 | tee /tmp/gh-output.txt + } + + if attempt_merge; then + exit 0 + fi + + # Not a real failure: requirements not yet met, or auto-merge already enabled. + if grep -qE "auto-merge is already enabled|[Rr]equired.*status.*check|[Rr]equired approving review|[Rr]equired.*review" /tmp/gh-output.txt; then + echo "Auto-merge not enabled yet - this is expected when requirements are not met or already enabled" + exit 0 + fi + + # The repository-level "Allow auto-merge" setting is off. Re-enable it and retry once. + # This can happen if the setting is accidentally toggled in the repository admin UI. + if grep -qF "Auto merge is not allowed for this repository" /tmp/gh-output.txt; then + echo "Repository Allow auto-merge is disabled; attempting to re-enable..." + if ! gh api "repos/${GITHUB_REPOSITORY}" --method PATCH --field allow_auto_merge=true > /dev/null; then + echo "Could not re-enable Allow auto-merge on the repository. Manual intervention required." + exit 1 + fi + echo "Repository setting re-enabled; retrying..." + if ! attempt_merge; then + echo "Auto-merge still failed after re-enabling repository setting:" cat /tmp/gh-output.txt exit 1 fi + exit 0 fi + + # Concurrent merge race: another PR merged while this one was being processed, + # moving the base branch. Request a Dependabot rebase to update this branch; + # this workflow re-triggers naturally via check_suite:completed when the rebased + # branch passes checks. With N concurrent Dependabot PRs, this converges in N-1 + # rounds: each round merges one PR, and any PRs that fall behind again each get + # a fresh rebase request on the next trigger. + if grep -qF "Base branch was modified" /tmp/gh-output.txt; then + echo "Base branch was modified by a concurrent merge. Requesting Dependabot rebase..." + gh pr comment "${PR_NUMBER}" --repo "$GITHUB_REPOSITORY" --body "@dependabot rebase" || true + echo "Rebase requested; workflow will re-trigger when the rebased branch passes checks." + exit 0 + fi + + # Unexpected error. + echo "Unexpected error enabling auto-merge:" + cat /tmp/gh-output.txt + exit 1 # yamllint enable