From 1fb12d0d61e80e516c7af4c1a1b9d482509ca3d8 Mon Sep 17 00:00:00 2001 From: DemchaAV Date: Mon, 6 Jul 2026 18:25:52 +0100 Subject: [PATCH] ci: run selective jobs on pull requests, full gate on push Add dorny/paths-filter change detection so a pull request only runs the jobs its change can affect: a docs-only PR runs the guards alone, Binary Compatibility runs only when the graph-compose-core module changed, and the Performance Smoke Check only when core / render-pdf / templates changed. Pushes to main / develop / 2.0-dev and manual dispatch always run the full reactor build + examples (defence in depth). Add a CI Gate job that aggregates the selective jobs into one stable status check, green when every job that ran passed (a job skipped by the filter counts as success). Point branch protection at CI Gate plus the always-run guards so a docs-only PR is not left waiting on a skipped matrix leg. --- .github/workflows/ci.yml | 83 ++++++++++++++++++++++++++++++++++++---- CONTRIBUTING.md | 6 ++- 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a73ab00..e2487ed7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,7 @@ on: branches: - main - develop + - 2.0-dev pull_request: # Run CI for any incoming PR regardless of the base branch so # feature branches targeting `develop` (e.g. v1.7 prep) also @@ -41,10 +42,53 @@ jobs: "-Dtest=EnginePdfBoundaryTest,CanonicalTemplateComposerPdfBoundaryTest,PdfRenderInterfaceGuardTest,DocumentationCoverageTest,DocumentationExamplesTest,CanonicalSurfaceGuardTest,TemplateComposeApiTest,VersionConsistencyGuardTest" \ test + changes: + # Path-based change detection for selective CI on pull requests. Emits the + # reverse-dependency flags the heavy jobs gate on: `code` (any build input), + # `core` (the root graph-compose-core module — drives japicmp), and `perf` + # (modules the smoke benchmark exercises). Pushes/dispatch bypass these gates. + name: Detect changed paths + if: github.event_name != 'schedule' + runs-on: ubuntu-latest + outputs: + code: ${{ steps.filter.outputs.code }} + core: ${{ steps.filter.outputs.core }} + perf: ${{ steps.filter.outputs.perf }} + steps: + - name: Check out repository + uses: actions/checkout@v7 + + - name: Detect changed module paths + uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + code: + - '**/*.java' + - '**/pom.xml' + - '**/src/main/resources/**' + - '**/src/test/resources/**' + - '.mvn/**' + - 'mvnw' + - 'mvnw.cmd' + - '.github/workflows/ci.yml' + core: + - 'src/**' + - 'pom.xml' + perf: + - 'src/**' + - 'render-pdf/**' + - 'templates/**' + - 'benchmarks/**' + - 'pom.xml' + build-and-test: name: Build and run tests (JDK ${{ matrix.java }}) - if: github.event_name != 'schedule' - needs: architecture-and-documentation-guards + # Selective CI: on a pull request, skip the heavy reactor build + JDK matrix + # when only docs / non-build files changed (`code` is false). Pushes to the + # integration branches and manual dispatch always run the full gate. + if: github.event_name != 'schedule' && (github.event_name != 'pull_request' || needs.changes.outputs.code == 'true') + needs: [architecture-and-documentation-guards, changes] runs-on: ubuntu-latest strategy: # Don't cancel sibling matrix jobs on the first failure — a @@ -87,8 +131,9 @@ jobs: examples-generation: name: Examples Generation Smoke Test - if: github.event_name != 'schedule' - needs: build-and-test + # Follows build-and-test: skipped for a docs-only PR, runs otherwise. + if: github.event_name != 'schedule' && (github.event_name != 'pull_request' || needs.changes.outputs.code == 'true') + needs: [build-and-test, changes] runs-on: ubuntu-latest env: JAVA_TOOL_OPTIONS: -Djava.awt.headless=true @@ -174,8 +219,10 @@ jobs: binary-compat: name: Binary Compatibility (japicmp vs pom baseline) - if: github.event_name == 'pull_request' - needs: architecture-and-documentation-guards + # japicmp diffs the graph-compose-core public surface, so it only matters + # when the core module (root `src/**` or `pom.xml`) changed. + if: github.event_name == 'pull_request' && needs.changes.outputs.core == 'true' + needs: [architecture-and-documentation-guards, changes] runs-on: ubuntu-latest env: JAVA_TOOL_OPTIONS: -Djava.awt.headless=true @@ -211,8 +258,10 @@ jobs: perf-smoke: name: Performance Smoke Check - if: github.event_name == 'pull_request' - needs: architecture-and-documentation-guards + # The smoke benchmark only exercises core + render-pdf + templates (built via + # the benchmarks module), so skip it when none of those changed. + if: github.event_name == 'pull_request' && needs.changes.outputs.perf == 'true' + needs: [architecture-and-documentation-guards, changes] runs-on: ubuntu-latest env: JAVA_TOOL_OPTIONS: -Djava.awt.headless=true @@ -278,6 +327,24 @@ jobs: path: benchmarks/target/surefire-reports/** if-no-files-found: ignore + ci-gate: + name: CI Gate + # Single stable status check aggregating the selective jobs above. Green when + # every job that ran succeeded; a job legitimately skipped by the path filter + # counts as success. Point branch protection at "CI Gate" (plus the always-run + # "Architecture and Documentation Guards") instead of the individual, sometimes + # -skipped matrix legs so a docs-only PR is never left waiting on a check that + # never reports. + if: always() && github.event_name != 'schedule' + needs: [architecture-and-documentation-guards, build-and-test, examples-generation, binary-compat, perf-smoke] + runs-on: ubuntu-latest + steps: + - name: Fail if any required job failed + if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') + run: | + echo "A required CI job failed or was cancelled: ${{ join(needs.*.result, ', ') }}" + exit 1 + benchmark-diff: name: Weekly Benchmark Diff if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fa8a550e..521adbe6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,10 +55,14 @@ GraphCompose follows a fork → feature branch → pull request flow. Exte This runs the architecture-and-documentation guards plus the full test suite. The same gate runs in CI on every PR. 5. **Push** your feature branch to your fork and open a pull request against `develop` on `DemchaAV/GraphCompose`. Reference any related issue and describe the user-visible change in the PR body. 6. **CI runs automatically.** Active jobs: - - `Architecture and Documentation Guards` — fast canonical / engine-boundary guard tests, fail-first gate + - `Architecture and Documentation Guards` — fast canonical / engine-boundary guard tests, fail-first gate (always runs) - `Build and run tests (JDK 17)`, `(JDK 21)`, `(JDK 25)` — full `mvnw verify` in parallel matrix across the supported JVMs - `Examples Generation Smoke Test` — regenerates all 54 runnable examples and uploads the PDFs as a CI artifact + - `Binary Compatibility` — PR-only japicmp diff of the `graph-compose-core` surface - `Performance Smoke Check` — PR-only coarse benchmark to catch performance regressions + - `CI Gate` — single aggregate status check that is green when every job that ran passed + + **Selective on pull requests:** a `dorny/paths-filter` step skips the heavy jobs when a PR touches nothing that affects the build — a **docs-only PR runs the guards only**; `Binary Compatibility` runs only when the core module changed, and the `Performance Smoke Check` only when core / render-pdf / templates changed. Pushes to `2.0-dev` / `main` (and manual dispatch) always run the full gate. Point branch protection at **`CI Gate`** + **`Architecture and Documentation Guards`** rather than the individual matrix legs, so a docs-only PR is not left waiting on a skipped check. The PR cannot merge into a protected branch until all required checks are green. 7. **Address review comments**, then squash any fixup commits before merge. The maintainer merges through GitHub once review is complete.